From ce12a1e017522edaad11845f5cc61713b3644ad5 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Tue, 23 Nov 2021 16:11:07 -0500 Subject: [PATCH] Add setMetatile functions to API (layerType, encounterType, terrainType, Behavior, Tile) --- include/core/metatile.h | 22 ++++++ include/mainwindow.h | 7 +- include/scripting.h | 1 + src/mainwindow_scriptapi.cpp | 128 ++++++++++++++++++++++++++--------- src/scripting.cpp | 15 ++++ src/ui/tileseteditor.cpp | 22 +++--- 6 files changed, 150 insertions(+), 45 deletions(-) diff --git a/include/core/metatile.h b/include/core/metatile.h index 5b9bb813..0b2af276 100644 --- a/include/core/metatile.h +++ b/include/core/metatile.h @@ -7,6 +7,28 @@ #include #include +enum { + METATILE_LAYER_MIDDLE_TOP, + METATILE_LAYER_BOTTOM_MIDDLE, + METATILE_LAYER_BOTTOM_TOP, + NUM_METATILE_LAYER_TYPES +}; + +enum { + ENCOUNTER_NONE, + ENCOUNTER_LAND, + ENCOUNTER_WATER, + NUM_METATILE_ENCOUNTER_TYPES +}; + +enum { + TERRAIN_NONE, + TERRAIN_GRASS, + TERRAIN_WATER, + TERRAIN_WATERFALL, + NUM_METATILE_TERRAIN_TYPES +}; + class Metatile { public: diff --git a/include/mainwindow.h b/include/mainwindow.h index 58785dcb..21c49e4a 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -112,6 +112,8 @@ public: Q_INVOKABLE void setMetatileLayerOrder(QList order); Q_INVOKABLE QList getMetatileLayerOpacity(); Q_INVOKABLE void setMetatileLayerOpacity(QList order); + void saveMetatilesByMetatileId(int metatileId); + void saveMetatileAttributesByMetatileId(int metatileId); Metatile * getMetatile(int metatileId); Q_INVOKABLE QString getMetatileLabel(int metatileId); Q_INVOKABLE void setMetatileLabel(int metatileId, QString label); @@ -124,7 +126,8 @@ public: Q_INVOKABLE int getMetatileBehavior(int metatileId); Q_INVOKABLE void setMetatileBehavior(int metatileId, int behavior); Q_INVOKABLE QJSValue getMetatileTile(int metatileId, int tileIndex); - Q_INVOKABLE void setMetatileTile(int metatileId, int tileIndex, int tile, bool xflip, bool yflip, int palette); + Q_INVOKABLE void setMetatileTile(int metatileId, int tileIndex, int tileId, bool xflip, bool yflip, int palette, bool forceRedraw = true); + Q_INVOKABLE void setMetatileTile(int metatileId, int tileIndex, QJSValue obj, bool forceRedraw = true); private slots: @@ -304,6 +307,8 @@ private: MapSortOrder mapSortOrder; + bool needsFullRedraw = false; + bool setMap(QString, bool scrollTreeView = false); void redrawMapScene(); void refreshMapScene(); diff --git a/include/scripting.h b/include/scripting.h index b45f065f..570e9c66 100644 --- a/include/scripting.h +++ b/include/scripting.h @@ -21,6 +21,7 @@ public: Scripting(MainWindow *mainWindow); static QJSValue fromBlock(Block block); static QJSValue fromTile(Tile tile); + static Tile toTile(QJSValue obj); static QJSValue dimensions(int width, int height); static QJSEngine *getEngine(); static void init(MainWindow *mainWindow); diff --git a/src/mainwindow_scriptapi.cpp b/src/mainwindow_scriptapi.cpp index 6ee2de4a..90c2df55 100644 --- a/src/mainwindow_scriptapi.cpp +++ b/src/mainwindow_scriptapi.cpp @@ -15,7 +15,15 @@ QJSValue MainWindow::getBlock(int x, int y) { } void MainWindow::tryRedrawMapArea(bool forceRedraw) { - if (forceRedraw) { + if (!forceRedraw) return; + + if (this->needsFullRedraw) { + this->editor->map_item->draw(true); + this->editor->collision_item->draw(true); + this->editor->updateMapBorder(); + this->editor->updateMapConnections(); + this->needsFullRedraw = false; + } else { this->editor->map_item->draw(); this->editor->collision_item->draw(); } @@ -541,6 +549,32 @@ void MainWindow::setMetatileLayerOpacity(QList order) { this->refreshAfterPalettePreviewChange(); } +void MainWindow::saveMetatilesByMetatileId(int metatileId) { + Tileset * tileset = Tileset::getBlockTileset(metatileId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + if (this->editor->project) + this->editor->project->saveTilesetMetatiles(tileset); + + // Refresh anything that can display metatiles (except the actual map view) + if (this->tilesetEditor) + this->tilesetEditor->updateTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); + if (this->editor->metatile_selector_item) + this->editor->metatile_selector_item->draw(); + if (this->editor->selected_border_metatiles_item) + this->editor->selected_border_metatiles_item->draw(); + if (this->editor->current_metatile_selection_item) + this->editor->current_metatile_selection_item->draw(); +} + +void MainWindow::saveMetatileAttributesByMetatileId(int metatileId) { + Tileset * tileset = Tileset::getBlockTileset(metatileId, this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); + if (this->editor->project) + this->editor->project->saveTilesetMetatileAttributes(tileset); + + // If the Tileset Editor is currently displaying the updated metatile, refresh it + if (this->tilesetEditor && this->tilesetEditor->getSelectedMetatile() == metatileId) + this->tilesetEditor->updateTilesets(this->editor->map->layout->tileset_primary_label, this->editor->map->layout->tileset_secondary_label); +} + Metatile * MainWindow::getMetatile(int metatileId) { if (!this->editor || !this->editor->map || !this->editor->map->layout) return nullptr; @@ -548,101 +582,129 @@ Metatile * MainWindow::getMetatile(int metatileId) { } QString MainWindow::getMetatileLabel(int metatileId) { - Metatile * metatile = getMetatile(metatileId); + Metatile * metatile = this->getMetatile(metatileId); if (!metatile || metatile->label.size() == 0) return QString(); return metatile->label; } +// TODO: Validate label void MainWindow::setMetatileLabel(int metatileId, QString label) { - Metatile * metatile = getMetatile(metatileId); + Metatile * metatile = this->getMetatile(metatileId); if (!metatile) return; - // TODO: Verify input - //label.remove(QRegularExpression("?![_A-Za-z0-9]*$")); - if (this->tilesetEditor && this->tilesetEditor->getSelectedMetatile() == metatileId) { this->tilesetEditor->setMetatileLabel(label); } else if (metatile->label != label) { metatile->label = label; - - // TODO: Writing to file immediately. - // Perhaps update tilesets to carry a "hasUnsavedChanges" field, - // and on saving the project save all tilesets with unsaved changes. - // saveTilesetMetatileLabels can be trivially updated to handle a single tileset at a time. if (this->editor->project) this->editor->project->saveTilesetMetatileLabels(this->editor->map->layout->tileset_primary, this->editor->map->layout->tileset_secondary); } } int MainWindow::getMetatileLayerType(int metatileId) { - Metatile * metatile = getMetatile(metatileId); + Metatile * metatile = this->getMetatile(metatileId); if (!metatile) return -1; return metatile->layerType; } void MainWindow::setMetatileLayerType(int metatileId, int layerType) { - // TODO - Metatile * metatile = getMetatile(metatileId); - if (!metatile) + Metatile * metatile = this->getMetatile(metatileId); + uint8_t u_layerType = static_cast(layerType); + if (!metatile || metatile->layerType == u_layerType || u_layerType >= NUM_METATILE_LAYER_TYPES) return; + metatile->layerType = u_layerType; + this->saveMetatileAttributesByMetatileId(metatileId); } int MainWindow::getMetatileEncounterType(int metatileId) { - Metatile * metatile = getMetatile(metatileId); + Metatile * metatile = this->getMetatile(metatileId); if (!metatile) return -1; return metatile->encounterType; } void MainWindow::setMetatileEncounterType(int metatileId, int encounterType) { - // TODO - Metatile * metatile = getMetatile(metatileId); - if (!metatile) + Metatile * metatile = this->getMetatile(metatileId); + uint8_t u_encounterType = static_cast(encounterType); + if (!metatile || metatile->encounterType == u_encounterType || u_encounterType >= NUM_METATILE_ENCOUNTER_TYPES) return; + metatile->encounterType = u_encounterType; + this->saveMetatileAttributesByMetatileId(metatileId); } int MainWindow::getMetatileTerrainType(int metatileId) { - Metatile * metatile = getMetatile(metatileId); + Metatile * metatile = this->getMetatile(metatileId); if (!metatile) return -1; return metatile->terrainType; } void MainWindow::setMetatileTerrainType(int metatileId, int terrainType) { - // TODO - Metatile * metatile = getMetatile(metatileId); - if (!metatile) + Metatile * metatile = this->getMetatile(metatileId); + uint8_t u_terrainType = static_cast(terrainType); + if (!metatile || metatile->terrainType == u_terrainType || u_terrainType >= NUM_METATILE_TERRAIN_TYPES) return; + metatile->terrainType = u_terrainType; + this->saveMetatileAttributesByMetatileId(metatileId); } int MainWindow::getMetatileBehavior(int metatileId) { - Metatile * metatile = getMetatile(metatileId); + Metatile * metatile = this->getMetatile(metatileId); if (!metatile) return -1; return metatile->behavior; } void MainWindow::setMetatileBehavior(int metatileId, int behavior) { - // TODO - Metatile * metatile = getMetatile(metatileId); - if (!metatile) + Metatile * metatile = this->getMetatile(metatileId); + uint16_t u_behavior = static_cast(behavior); + if (!metatile || metatile->behavior == u_behavior) return; + metatile->behavior = u_behavior; + this->saveMetatileAttributesByMetatileId(metatileId); } QJSValue MainWindow::getMetatileTile(int metatileId, int tileIndex) { - Metatile * metatile = getMetatile(metatileId); + Metatile * metatile = this->getMetatile(metatileId); int maxTileIndex = projectConfig.getTripleLayerMetatilesEnabled() ? 12 : 8; - if (!metatile || tileIndex >= maxTileIndex) + if (!metatile || tileIndex >= maxTileIndex || tileIndex < 0) return QJSValue(); return Scripting::fromTile(metatile->tiles[tileIndex]); } -void MainWindow::setMetatileTile(int metatileId, int tileIndex, int tile, bool xflip, bool yflip, int palette) { - // TODO - Metatile * metatile = getMetatile(metatileId); - if (!metatile) +void MainWindow::setMetatileTile(int metatileId, int tileIndex, int tileId, bool xflip, bool yflip, int palette, bool forceRedraw) { + Metatile * metatile = this->getMetatile(metatileId); + int maxTileIndex = projectConfig.getTripleLayerMetatilesEnabled() ? 12 : 8; + if (!metatile || tileIndex >= maxTileIndex || tileIndex < 0) return; + Tile * tile = &metatile->tiles[tileIndex]; + if (!tile) return; + if (tile->tile == tileId + && tile->xflip == xflip + && tile->yflip == yflip + && tile->palette == palette) + return; + + tile->tile = tileId; + tile->xflip = xflip; + tile->yflip = yflip; + tile->palette = palette; + this->saveMetatilesByMetatileId(metatileId); + + // TODO: Making tryRedrawMapArea do a full draw is unnecessarily expensive. + // The map metatiles that need to be updated are not changed by the above + // operation, so they will not be redrawn if the cache isn't ignored. + // Ideally setMetatileTile would properly set each of the map spaces that + // use this metatile so that the cache could be used, though this would + // likely still require a full read of the map and its border/connections. + this->needsFullRedraw = true; + this->tryRedrawMapArea(forceRedraw); +} + +void MainWindow::setMetatileTile(int metatileId, int tileIndex, QJSValue obj, bool forceRedraw) { + Tile tile = Scripting::toTile(obj); + this->setMetatileTile(metatileId, tileIndex, tile.tile, tile.xflip, tile.yflip, tile.palette, forceRedraw); } diff --git a/src/scripting.cpp b/src/scripting.cpp index 77a57a80..404bf7cb 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -156,6 +156,21 @@ QJSValue Scripting::dimensions(int width, int height) { return obj; } +Tile Scripting::toTile(QJSValue obj) { + if (!obj.hasProperty("tile") + || !obj.hasProperty("xflip") + || !obj.hasProperty("yflip") + || !obj.hasProperty("palette")) { + return Tile(); + } + Tile tile = Tile(); + tile.tile = obj.property("tile").toInt(); + tile.xflip = obj.property("xflip").toBool(); + tile.yflip = obj.property("yflip").toBool(); + tile.palette = obj.property("palette").toInt(); + return tile; +} + QJSValue Scripting::fromTile(Tile tile) { QJSValue obj = instance->engine->newObject(); obj.setProperty("tile", tile.tile); diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index 2ad25a76..1364aebf 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -120,9 +120,9 @@ void TilesetEditor::setMetatileBehaviors() { void TilesetEditor::setMetatileLayersUi() { if (!projectConfig.getTripleLayerMetatilesEnabled()) { - this->ui->comboBox_layerType->addItem("Normal - Middle/Top", 0); - this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", 1); - this->ui->comboBox_layerType->addItem("Split - Bottom/Top", 2); + this->ui->comboBox_layerType->addItem("Normal - Middle/Top", METATILE_LAYER_MIDDLE_TOP); + this->ui->comboBox_layerType->addItem("Covered - Bottom/Middle", METATILE_LAYER_BOTTOM_MIDDLE); + this->ui->comboBox_layerType->addItem("Split - Bottom/Top", METATILE_LAYER_BOTTOM_TOP); } else { this->ui->comboBox_layerType->setVisible(false); this->ui->label_layerType->setVisible(false); @@ -134,15 +134,15 @@ void TilesetEditor::setVersionSpecificUi() { if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokefirered) { this->ui->comboBox_encounterType->setVisible(true); this->ui->label_encounterType->setVisible(true); - this->ui->comboBox_encounterType->addItem("None", 0); - this->ui->comboBox_encounterType->addItem("Land", 1); - this->ui->comboBox_encounterType->addItem("Water", 2); + this->ui->comboBox_encounterType->addItem("None", ENCOUNTER_NONE); + this->ui->comboBox_encounterType->addItem("Land", ENCOUNTER_LAND); + this->ui->comboBox_encounterType->addItem("Water", ENCOUNTER_WATER); this->ui->comboBox_terrainType->setVisible(true); this->ui->label_terrainType->setVisible(true); - this->ui->comboBox_terrainType->addItem("Normal", 0); - this->ui->comboBox_terrainType->addItem("Grass", 1); - this->ui->comboBox_terrainType->addItem("Water", 2); - this->ui->comboBox_terrainType->addItem("Waterfall", 3); + this->ui->comboBox_terrainType->addItem("Normal", TERRAIN_NONE); + this->ui->comboBox_terrainType->addItem("Grass", TERRAIN_GRASS); + this->ui->comboBox_terrainType->addItem("Water", TERRAIN_WATER); + this->ui->comboBox_terrainType->addItem("Waterfall", TERRAIN_WATERFALL); } else { this->ui->comboBox_encounterType->setVisible(false); this->ui->label_encounterType->setVisible(false); @@ -485,7 +485,7 @@ void TilesetEditor::on_comboBox_metatileBehaviors_textActivated(const QString &m { if (this->metatile) { Metatile *prevMetatile = new Metatile(*this->metatile); - this->metatile->behavior = static_cast(project->metatileBehaviorMap[metatileBehavior]); + this->metatile->behavior = static_cast(project->metatileBehaviorMap[metatileBehavior]); MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatile(), prevMetatile, new Metatile(*this->metatile)); metatileHistory.push(commit);