diff --git a/forms/regionmapeditor.ui b/forms/regionmapeditor.ui
index 45e43bff..5408a32d 100644
--- a/forms/regionmapeditor.ui
+++ b/forms/regionmapeditor.ui
@@ -1101,6 +1101,7 @@
+
diff --git a/include/core/regionmap.h b/include/core/regionmap.h
index bb0c7f56..3501cc28 100644
--- a/include/core/regionmap.h
+++ b/include/core/regionmap.h
@@ -55,6 +55,8 @@ public:
bool loadEntries();
void setEntries(tsl::ordered_map *entries) { this->region_map_entries = entries; }
+ void setEntries(tsl::ordered_map entries) { *(this->region_map_entries) = entries; }
+ void clearEntries() { this->region_map_entries->clear(); }
MapSectionEntry getEntry(QString section);
void setEntry(QString section, MapSectionEntry entry);
void removeEntry(QString section);
diff --git a/include/core/regionmapeditcommands.h b/include/core/regionmapeditcommands.h
index 7f25d938..16cd6486 100644
--- a/include/core/regionmapeditcommands.h
+++ b/include/core/regionmapeditcommands.h
@@ -17,6 +17,7 @@ enum RMCommandId {
ID_RemoveEntry,
ID_AddEntry,
ID_ResizeTilemap,
+ ID_ClearEntries,
};
@@ -149,4 +150,20 @@ private:
};
+/// ClearEntries
+class ClearEntries : public QUndoCommand {
+public:
+ ClearEntries(RegionMap *map, tsl::ordered_map, QUndoCommand *parent = nullptr);
+
+ void undo() override;
+ void redo() override;
+
+ bool mergeWith(const QUndoCommand *command) override { return false; }
+ int id() const override { return RMCommandId::ID_ClearEntries; }
+
+private:
+ RegionMap *map;
+ tsl::ordered_map entries;
+};
+
#endif // REGIONMAPEDITCOMMANDS_H
diff --git a/include/ui/regionmapeditor.h b/include/ui/regionmapeditor.h
index dfcc1f87..25185619 100644
--- a/include/ui/regionmapeditor.h
+++ b/include/ui/regionmapeditor.h
@@ -119,6 +119,7 @@ private slots:
void on_action_RegionMap_Resize_triggered();
void on_action_RegionMap_ClearImage_triggered();
void on_action_RegionMap_ClearLayout_triggered();
+ void on_action_RegionMap_ClearEntries_triggered();
void on_action_Swap_triggered();
void on_tabWidget_Region_Map_currentChanged(int);
void on_pushButton_RM_Options_delete_clicked();
diff --git a/include/ui/regionmappixmapitem.h b/include/ui/regionmappixmapitem.h
index 1856a8bb..7740fa86 100644
--- a/include/ui/regionmappixmapitem.h
+++ b/include/ui/regionmappixmapitem.h
@@ -21,8 +21,10 @@ public:
TilemapTileSelector *tile_selector;
virtual void paint(QGraphicsSceneMouseEvent *);
+ virtual void fill(QGraphicsSceneMouseEvent *);
virtual void select(QGraphicsSceneMouseEvent *);
virtual void draw();
+ void floodFill(int x, int y, std::shared_ptr oldTile, std::shared_ptr newTile);
signals:
void mouseEvent(QGraphicsSceneMouseEvent *, RegionMapPixmapItem *);
diff --git a/include/ui/tilemaptileselector.h b/include/ui/tilemaptileselector.h
index 9c2ba1af..f68a6974 100644
--- a/include/ui/tilemaptileselector.h
+++ b/include/ui/tilemaptileselector.h
@@ -60,6 +60,10 @@ public:
virtual void setVFlip(bool vFlip) { this->vFlip_ = vFlip; }
virtual void setPalette(int palette) { this->palette_ = palette; }
+ bool operator==(const TilemapTile& other) {
+ return (this->raw() == other.raw());
+ }
+
virtual QString info() const {
return QString("Tile: 0x") + QString("%1 ").arg(this->id(), 4, 16, QChar('0')).toUpper();
}
@@ -132,7 +136,7 @@ public:
setAcceptHoverEvents(true);
}
void draw();
- void setPalette();
+ QImage setPalette(int index);
int pixelWidth;
int pixelHeight;
@@ -146,7 +150,10 @@ public:
void selectHFlip(bool vFlip) { this->tile_vFlip = vFlip; }
bool tile_vFlip = false;
- void selectPalette(int palette) { this->tile_palette = palette; }
+ void selectPalette(int palette) {
+ this->tile_palette = palette;
+ this->draw();
+ }
int tile_palette = 0;
QImage tileset;
diff --git a/src/core/regionmap.cpp b/src/core/regionmap.cpp
index d656507f..18b877d1 100644
--- a/src/core/regionmap.cpp
+++ b/src/core/regionmap.cpp
@@ -549,6 +549,8 @@ void RegionMap::setLayoutDimensions(int width, int height, bool update) {
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++) {
LayoutSquare newSquare;
+ newSquare.x = x;
+ newSquare.y = y;
if (x < this->layout_width && y < this->layout_height) {
// within old layout
int oldIndex = this->get_layout_index(x, y);
diff --git a/src/core/regionmapeditcommands.cpp b/src/core/regionmapeditcommands.cpp
index 654a73b4..a6e95c3f 100644
--- a/src/core/regionmapeditcommands.cpp
+++ b/src/core/regionmapeditcommands.cpp
@@ -258,4 +258,29 @@ void ResizeTilemap::undo() {
QUndoCommand::undo();
}
+///
+
+ClearEntries::ClearEntries(RegionMap *map, tsl::ordered_map entries, QUndoCommand *parent) {
+ setText("Clear Entries");
+
+ this->map = map;
+ this->entries = entries;
+}
+
+void ClearEntries::redo() {
+ QUndoCommand::redo();
+
+ if (!map) return;
+
+ map->clearEntries();
+}
+
+void ClearEntries::undo() {
+ if (!map) return;
+
+ map->setEntries(entries);
+
+ QUndoCommand::undo();
+}
+
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index eaebb3e7..73ef97c4 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -3147,6 +3147,11 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() {
bool MainWindow::initRegionMapEditor() {
this->regionMapEditor = new RegionMapEditor(this, this->editor->project);
+ this->regionMapEditor->setAttribute(Qt::WA_DeleteOnClose);
+ connect(this->regionMapEditor, &QObject::destroyed, [this](){
+ this->regionMapEditor = nullptr;
+ });
+
bool success = this->regionMapEditor->load();
if (!success) {
delete this->regionMapEditor;
diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp
index 84c18b9a..31a1c453 100644
--- a/src/ui/regionmapeditor.cpp
+++ b/src/ui/regionmapeditor.cpp
@@ -482,10 +482,9 @@ void RegionMapEditor::setRegionMap(RegionMap *map) {
} else {
this->ui->tabWidget_Region_Map->setTabEnabled(1, false);
this->ui->tabWidget_Region_Map->setTabEnabled(2, false);
+ this->ui->tabWidget_Region_Map->setCurrentIndex(0);
}
- this->ui->tabWidget_Region_Map->setCurrentIndex(0);
-
displayRegionMap();
}
@@ -685,27 +684,27 @@ void RegionMapEditor::displayRegionMapEntriesImage() {
void RegionMapEditor::displayRegionMapEntryOptions() {
if (!this->region_map->layoutEnabled()) return;
+ this->ui->comboBox_RM_Entry_MapSection->clear();
this->ui->comboBox_RM_Entry_MapSection->addItems(this->project->mapSectionValueToName.values());
- int width = this->region_map->tilemapWidth() - this->region_map->padLeft() - this->region_map->padRight();
- int height = this->region_map->tilemapHeight() - this->region_map->padTop() - this->region_map->padBottom();
- this->ui->spinBox_RM_Entry_x->setMaximum(width - 1);
- this->ui->spinBox_RM_Entry_y->setMaximum(height - 1);
+ this->ui->spinBox_RM_Entry_x->setMaximum(128);
+ this->ui->spinBox_RM_Entry_y->setMaximum(128);
this->ui->spinBox_RM_Entry_width->setMinimum(1);
this->ui->spinBox_RM_Entry_height->setMinimum(1);
- this->ui->spinBox_RM_Entry_width->setMaximum(width);
- this->ui->spinBox_RM_Entry_height->setMaximum(height);
+ this->ui->spinBox_RM_Entry_width->setMaximum(128);
+ this->ui->spinBox_RM_Entry_height->setMaximum(128);
}
void RegionMapEditor::updateRegionMapEntryOptions(QString section) {
if (!this->region_map->layoutEnabled()) return;
- bool enabled = (section != "MAPSEC_NONE") && (this->region_map_entries.contains(section));
+ bool enabled = ((section != "MAPSEC_NONE") && (section != "MAPSEC_COUNT")) && (this->region_map_entries.contains(section));
this->ui->lineEdit_RM_MapName->setEnabled(enabled);
this->ui->spinBox_RM_Entry_x->setEnabled(enabled);
this->ui->spinBox_RM_Entry_y->setEnabled(enabled);
this->ui->spinBox_RM_Entry_width->setEnabled(enabled);
this->ui->spinBox_RM_Entry_height->setEnabled(enabled);
+ this->ui->pushButton_entryActivate->setEnabled(section != "MAPSEC_NONE" && section != "MAPSEC_COUNT");
this->ui->pushButton_entryActivate->setText(enabled ? "Remove" : "Add");
this->ui->lineEdit_RM_MapName->blockSignals(true);
@@ -862,7 +861,22 @@ void RegionMapEditor::mouseEvent_region_map(QGraphicsSceneMouseEvent *event, Reg
if (event->buttons() & Qt::RightButton) {
item->select(event);
- //} else if (event->buttons() & Qt::MiddleButton) {// TODO
+ // set palette and flips
+ auto tile = this->region_map->getTile(x, y);
+ this->ui->spinBox_tilePalette->setValue(tile->palette());
+ this->ui->checkBox_tileHFlip->setChecked(tile->hFlip());
+ this->ui->checkBox_tileVFlip->setChecked(tile->vFlip());
+ } else if (event->modifiers() & Qt::ControlModifier) {
+ if (event->type() == QEvent::GraphicsSceneMouseRelease) {
+ actionId_++;
+ } else {
+ QByteArray oldTilemap = this->region_map->getTilemap();
+ item->fill(event);
+ QByteArray newTilemap = this->region_map->getTilemap();
+ EditTilemap *command = new EditTilemap(this->region_map, oldTilemap, newTilemap, actionId_);
+ command->setText("Fill Tilemap");
+ this->region_map->commit(command);
+ }
} else {
if (event->type() == QEvent::GraphicsSceneMouseRelease) {
actionId_++;
@@ -872,7 +886,6 @@ void RegionMapEditor::mouseEvent_region_map(QGraphicsSceneMouseEvent *event, Reg
QByteArray newTilemap = this->region_map->getTilemap();
EditTilemap *command = new EditTilemap(this->region_map, oldTilemap, newTilemap, actionId_);
this->region_map->commit(command);
- //this->region_map_layout_item->draw();
}
}
}
@@ -942,7 +955,6 @@ void RegionMapEditor::on_spinBox_RM_Entry_x_valueChanged(int x) {
this->region_map_entries[activeEntry].y + this->region_map->padTop());
this->region_map_entries_item->select(idx);
this->region_map_entries_item->draw();
- this->ui->spinBox_RM_Entry_width->setMaximum(this->region_map->tilemapWidth() - this->region_map->padLeft() - this->region_map->padRight() - x);
}
void RegionMapEditor::on_spinBox_RM_Entry_y_valueChanged(int y) {
@@ -956,7 +968,6 @@ void RegionMapEditor::on_spinBox_RM_Entry_y_valueChanged(int y) {
this->region_map_entries[activeEntry].y + this->region_map->padTop());
this->region_map_entries_item->select(idx);
this->region_map_entries_item->draw();
- this->ui->spinBox_RM_Entry_height->setMaximum(this->region_map->tilemapHeight() - this->region_map->padTop() - this->region_map->padBottom() - y);
}
void RegionMapEditor::on_spinBox_RM_Entry_width_valueChanged(int width) {
@@ -1166,6 +1177,24 @@ void RegionMapEditor::on_action_RegionMap_ClearLayout_triggered() {
}
}
+void RegionMapEditor::on_action_RegionMap_ClearEntries_triggered() {
+ QMessageBox::StandardButton result = QMessageBox::question(
+ this,
+ "WARNING",
+ "This action will remove the entire mapsection entries list, continue?",
+ QMessageBox::Yes | QMessageBox::Cancel,
+ QMessageBox::Yes
+ );
+
+ if (result == QMessageBox::Yes) {
+ ClearEntries *commit = new ClearEntries(this->region_map, this->region_map_entries);
+ this->region_map->editHistory.push(commit);
+ displayRegionMapLayout();
+ } else {
+ return;
+ }
+}
+
bool RegionMapEditor::modified() {
return !this->history.isClean();
}
diff --git a/src/ui/regionmapentriespixmapitem.cpp b/src/ui/regionmapentriespixmapitem.cpp
index b45ecfe7..779b73bb 100644
--- a/src/ui/regionmapentriespixmapitem.cpp
+++ b/src/ui/regionmapentriespixmapitem.cpp
@@ -54,7 +54,8 @@ void RegionMapEntriesPixmapItem::draw() {
this->selectionOffsetY = entry_h - 1;
this->setPixmap(QPixmap::fromImage(image));
- this->drawSelection();
+
+ if (selectingEntry) this->drawSelection();
}
void RegionMapEntriesPixmapItem::select(int x, int y) {
@@ -81,7 +82,6 @@ void RegionMapEntriesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event
int x = pos.x() - this->region_map->padLeft();
int y = pos.y() - this->region_map->padTop();
- //RegionMapEntry entry = this->region_map->mapSecToMapEntry.value(currentSection);
MapSectionEntry entry = this->region_map->getEntry(currentSection);
pressedX = x - entry.x;
pressedY = y - entry.y;
diff --git a/src/ui/regionmaplayoutpixmapitem.cpp b/src/ui/regionmaplayoutpixmapitem.cpp
index 3988fc3f..eda35152 100644
--- a/src/ui/regionmaplayoutpixmapitem.cpp
+++ b/src/ui/regionmaplayoutpixmapitem.cpp
@@ -62,9 +62,7 @@ void RegionMapLayoutPixmapItem::highlight(int x, int y, int red) {
void RegionMapLayoutPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
QPoint pos = this->getCellPos(event->pos());
- int index = this->region_map->getMapSquareIndex(pos.x(), pos.y());
- if (this->region_map->squareX(index) >= 0
- && this->region_map->squareY(index) >= 0) {
+ if (this->region_map->squareInLayout(pos.x(), pos.y())) {
SelectablePixmapItem::mousePressEvent(event);
this->updateSelectedTile();
emit selectedTileChanged(this->selectedTile);
diff --git a/src/ui/regionmappixmapitem.cpp b/src/ui/regionmappixmapitem.cpp
index a93e271a..6f481117 100644
--- a/src/ui/regionmappixmapitem.cpp
+++ b/src/ui/regionmappixmapitem.cpp
@@ -35,6 +35,70 @@ void RegionMapPixmapItem::paint(QGraphicsSceneMouseEvent *event) {
}
}
+void RegionMapPixmapItem::floodFill(int x, int y, std::shared_ptr oldTile, std::shared_ptr newTile) {
+ // out of bounds
+ if (x < 0
+ || y < 0
+ || x >= this->region_map->tilemapWidth()
+ || y >= this->region_map->tilemapHeight()) {
+ return;
+ }
+
+ auto tile = this->region_map->getTile(x, y);
+ if (!tile->operator==(*oldTile) || tile->operator==(*newTile)) {
+ return;
+ }
+
+ int index = x + y * this->region_map->tilemapWidth();
+ this->region_map->setTileData(index,
+ newTile->id(),
+ newTile->hFlip(),
+ newTile->vFlip(),
+ newTile->palette()
+ );
+
+ floodFill(x + 1, y, oldTile, newTile);
+ floodFill(x - 1, y, oldTile, newTile);
+ floodFill(x, y + 1, oldTile, newTile);
+ floodFill(x, y - 1, oldTile, newTile);
+}
+
+void RegionMapPixmapItem::fill(QGraphicsSceneMouseEvent *event) {
+ if (region_map) {
+ QPointF pos = event->pos();
+ int x = static_cast(pos.x()) / 8;
+ int y = static_cast(pos.y()) / 8;
+ int index = x + y * this->region_map->tilemapWidth();
+ std::shared_ptr currentTile, newTile, oldTile;
+ currentTile = this->region_map->getTile(index);
+ switch(this->tile_selector->format) {
+ case TilemapFormat::Plain:
+ oldTile = std::make_shared(currentTile->raw());
+ newTile = std::make_shared(currentTile->raw());
+ newTile->setId(this->tile_selector->selectedTile);
+ break;
+ case TilemapFormat::BPP_4:
+ oldTile = std::make_shared(currentTile->raw());
+ newTile = std::make_shared(currentTile->raw());
+ newTile->setId(this->tile_selector->selectedTile);
+ newTile->setHFlip(this->tile_selector->tile_hFlip);
+ newTile->setVFlip(this->tile_selector->tile_vFlip);
+ newTile->setPalette(this->tile_selector->tile_palette);
+ break;
+ case TilemapFormat::BPP_8:
+ oldTile = std::make_shared(currentTile->raw());
+ newTile = std::make_shared(currentTile->raw());
+ newTile->setId(this->tile_selector->selectedTile);
+ newTile->setId(this->tile_selector->selectedTile);
+ newTile->setHFlip(this->tile_selector->tile_hFlip);
+ newTile->setVFlip(this->tile_selector->tile_vFlip);
+ break;
+ }
+ floodFill(x, y, oldTile, newTile);
+ draw();
+ }
+}
+
void RegionMapPixmapItem::select(QGraphicsSceneMouseEvent *event) {
QPointF pos = event->pos();
int x = static_cast(pos.x()) / 8;
diff --git a/src/ui/tilemaptileselector.cpp b/src/ui/tilemaptileselector.cpp
index 96775e05..865e14ef 100644
--- a/src/ui/tilemaptileselector.cpp
+++ b/src/ui/tilemaptileselector.cpp
@@ -12,7 +12,7 @@ void TilemapTileSelector::draw() {
this->numTilesWide = width_ / 8;
this->numTiles = ntiles_;
- this->setPixmap(QPixmap::fromImage(tileset));
+ this->setPixmap(QPixmap::fromImage(this->setPalette(this->tile_palette)));
this->drawSelection();
}
@@ -39,11 +39,7 @@ QPoint TilemapTileSelector::getTileIdCoords(unsigned tileId) {
return QPoint(index % this->numTilesWide, index / this->numTilesWide);
}
-QImage TilemapTileSelector::tileImg(shared_ptr tile) {
- // TODO: this is slow on the entries tab, so maybe do not do all of the redraw for every section change
- unsigned tileId = tile->id();
- QPoint pos = getTileIdCoords(tileId);
-
+QImage TilemapTileSelector::setPalette(int paletteIndex) {
QImage tilesetImage = this->tileset;
tilesetImage.convertTo(QImage::Format::Format_Indexed8);
@@ -54,7 +50,7 @@ QImage TilemapTileSelector::tileImg(shared_ptr tile) {
case TilemapFormat::BPP_4:
{
QVector newColorTable;
- int palMinLength = tile->palette() * 16 + 16;
+ int palMinLength = paletteIndex * 16 + 16;
if ((this->palette.count() < palMinLength) || (tilesetImage.colorTable().count() != 16)) {
// either a) the palette has less than 256 colors, or b) the image is improperly indexed
for (QRgb color : tilesetImage.colorTable()) {
@@ -65,9 +61,9 @@ QImage TilemapTileSelector::tileImg(shared_ptr tile) {
// use actual pal
// before Qt 6, the color table is a QVector which is deprecated now, and this method does not exits
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- newColorTable = this->palette.toVector().mid(tile->palette() * 16, 16);
+ newColorTable = this->palette.toVector().mid(paletteIndex * 16, 16);
#else
- newColorTable = this->palette.mid(tile->palette() * 16, 16);
+ newColorTable = this->palette.mid(paletteIndex * 16, 16);
#endif
}
tilesetImage.setColorTable(newColorTable);
@@ -87,6 +83,15 @@ QImage TilemapTileSelector::tileImg(shared_ptr tile) {
default: break;
}
+ return tilesetImage;
+}
+
+QImage TilemapTileSelector::tileImg(shared_ptr tile) {
+ unsigned tileId = tile->id();
+ QPoint pos = getTileIdCoords(tileId);
+
+ QImage tilesetImage = setPalette(tile->palette());
+
// take a tile from the tileset
QImage img = tilesetImage.copy(pos.x() * 8, pos.y() * 8, 8, 8);
img = img.mirrored(tile->hFlip(), tile->vFlip());