Add setMetatile functions to API (layerType, encounterType, terrainType, Behavior, Tile)

This commit is contained in:
GriffinR 2021-11-23 16:11:07 -05:00 committed by huderlem
parent 26f0042ff4
commit ce12a1e017
6 changed files with 150 additions and 45 deletions

View file

@ -7,6 +7,28 @@
#include <QPoint>
#include <QString>
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:

View file

@ -112,6 +112,8 @@ public:
Q_INVOKABLE void setMetatileLayerOrder(QList<int> order);
Q_INVOKABLE QList<float> getMetatileLayerOpacity();
Q_INVOKABLE void setMetatileLayerOpacity(QList<float> 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();

View file

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

View file

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

View file

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

View file

@ -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<uint8_t>(project->metatileBehaviorMap[metatileBehavior]);
this->metatile->behavior = static_cast<uint16_t>(project->metatileBehaviorMap[metatileBehavior]);
MetatileHistoryItem *commit = new MetatileHistoryItem(this->getSelectedMetatile(),
prevMetatile, new Metatile(*this->metatile));
metatileHistory.push(commit);