From 813845e0dab9c6ba82c840461cf94bf729a4d9b0 Mon Sep 17 00:00:00 2001 From: garak Date: Thu, 5 May 2022 21:40:13 -0400 Subject: [PATCH] address feedback, fix oversights --- forms/regionmapeditor.ui | 6 +++ include/core/regionmap.h | 2 + include/core/regionmapeditcommands.h | 17 +++++++ include/ui/regionmapeditor.h | 1 + include/ui/regionmappixmapitem.h | 2 + include/ui/tilemaptileselector.h | 11 ++++- src/core/regionmap.cpp | 2 + src/core/regionmapeditcommands.cpp | 25 +++++++++++ src/mainwindow.cpp | 5 +++ src/ui/regionmapeditor.cpp | 55 +++++++++++++++++------ src/ui/regionmapentriespixmapitem.cpp | 4 +- src/ui/regionmaplayoutpixmapitem.cpp | 4 +- src/ui/regionmappixmapitem.cpp | 64 +++++++++++++++++++++++++++ src/ui/tilemaptileselector.cpp | 23 ++++++---- 14 files changed, 192 insertions(+), 29 deletions(-) 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 @@ + @@ -1163,6 +1164,11 @@ Save All + + + Clear Map Entries + + 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());