mirror of
https://github.com/huderlem/porymap.git
synced 2026-09-27 10:26:33 -05:00
Merge branch 'master' into api-redesign
This commit is contained in:
@@ -503,6 +503,8 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
||||
this->customScripts.append(script);
|
||||
}
|
||||
}
|
||||
} else if (key == "prefabs_filepath") {
|
||||
this->prefabFilepath = value;
|
||||
} else {
|
||||
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
|
||||
}
|
||||
@@ -540,6 +542,7 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
|
||||
map.insert("create_map_text_file", QString::number(this->createMapTextFile));
|
||||
map.insert("enable_triple_layer_metatiles", QString::number(this->enableTripleLayerMetatiles));
|
||||
map.insert("custom_scripts", this->customScripts.join(","));
|
||||
map.insert("prefabs_filepath", this->prefabFilepath);
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -732,6 +735,18 @@ QList<QString> ProjectConfig::getCustomScripts() {
|
||||
return this->customScripts;
|
||||
}
|
||||
|
||||
void ProjectConfig::setPrefabFilepath(QString filepath) {
|
||||
this->prefabFilepath = filepath;
|
||||
this->save();
|
||||
}
|
||||
|
||||
QString ProjectConfig::getPrefabFilepath() {
|
||||
if (this->prefabFilepath.isEmpty()) {
|
||||
this->setPrefabFilepath("prefabs.json");
|
||||
}
|
||||
return this->prefabFilepath;
|
||||
}
|
||||
|
||||
ShortcutsConfig shortcutsConfig;
|
||||
|
||||
QString ShortcutsConfig::getConfigFilepath() {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "editor.h"
|
||||
#include "eventpropertiesframe.h"
|
||||
#include "ui_eventpropertiesframe.h"
|
||||
#include "prefabcreationdialog.h"
|
||||
#include "bordermetatilespixmapitem.h"
|
||||
#include "currentselectedmetatilespixmapitem.h"
|
||||
#include "customattributestable.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "flowlayout.h"
|
||||
#include "shortcut.h"
|
||||
#include "mapparser.h"
|
||||
#include "prefab.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QClipboard>
|
||||
@@ -536,12 +538,9 @@ bool MainWindow::openProject(QString dir) {
|
||||
editor->project->clearTilesetCache();
|
||||
success = loadDataStructures() && populateMapList() && setMap(open_map, true);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
showWindowTitle();
|
||||
this->statusBar()->showMessage(QString("Opened project %1").arg(nativeDir));
|
||||
Scripting::cb_ProjectOpened(dir);
|
||||
} else {
|
||||
|
||||
projectOpenFailure = !success;
|
||||
if (projectOpenFailure) {
|
||||
this->statusBar()->showMessage(QString("Failed to open project %1").arg(nativeDir));
|
||||
QMessageBox msgBox(this);
|
||||
QString errorMsg = QString("There was an error opening the project %1. Please see %2 for full error details.\n\n%3")
|
||||
@@ -549,10 +548,19 @@ bool MainWindow::openProject(QString dir) {
|
||||
.arg(getLogPath())
|
||||
.arg(getMostRecentError());
|
||||
msgBox.critical(nullptr, "Error Opening Project", errorMsg);
|
||||
|
||||
return false;
|
||||
}
|
||||
projectOpenFailure = !success;
|
||||
return success;
|
||||
|
||||
showWindowTitle();
|
||||
this->statusBar()->showMessage(QString("Opened project %1").arg(nativeDir));
|
||||
|
||||
prefab.initPrefabUI(
|
||||
editor->metatile_selector_item,
|
||||
ui->scrollAreaWidgetContents_Prefabs,
|
||||
ui->label_prefabHelp,
|
||||
editor->map);
|
||||
Scripting::cb_ProjectOpened(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainWindow::isProjectOpen() {
|
||||
@@ -653,6 +661,7 @@ bool MainWindow::setMap(QString map_name, bool scrollTreeView) {
|
||||
updateMapList();
|
||||
|
||||
Scripting::cb_MapOpened(map_name);
|
||||
prefab.updatePrefabUi(editor->map);
|
||||
updateTilesetEditor();
|
||||
return true;
|
||||
}
|
||||
@@ -1374,7 +1383,8 @@ void MainWindow::redrawMetatileSelection()
|
||||
|
||||
QPoint size = editor->metatile_selector_item->getSelectionDimensions();
|
||||
if (size.x() == 1 && size.y() == 1) {
|
||||
QPoint pos = editor->metatile_selector_item->getMetatileIdCoordsOnWidget(editor->metatile_selector_item->getSelectedMetatiles()->at(0));
|
||||
MetatileSelection selection = editor->metatile_selector_item->getMetatileSelection();
|
||||
QPoint pos = editor->metatile_selector_item->getMetatileIdCoordsOnWidget(selection.metatileItems.first().metatileId);
|
||||
pos *= scale;
|
||||
ui->scrollArea_2->ensureVisible(pos.x(), pos.y(), 8 * scale, 8 * scale);
|
||||
}
|
||||
@@ -1383,8 +1393,10 @@ void MainWindow::redrawMetatileSelection()
|
||||
void MainWindow::currentMetatilesSelectionChanged()
|
||||
{
|
||||
redrawMetatileSelection();
|
||||
if (this->tilesetEditor)
|
||||
this->tilesetEditor->selectMetatile(editor->metatile_selector_item->getSelectedMetatiles()->at(0));
|
||||
if (this->tilesetEditor) {
|
||||
MetatileSelection selection = editor->metatile_selector_item->getMetatileSelection();
|
||||
this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_mapList_activated(const QModelIndex &index)
|
||||
@@ -1456,15 +1468,18 @@ void MainWindow::copy() {
|
||||
OrderedJson::object copyObject;
|
||||
copyObject["object"] = "metatile_selection";
|
||||
OrderedJson::array metatiles;
|
||||
for (auto item : *editor->metatile_selector_item->getSelectedMetatiles()) {
|
||||
metatiles.append(static_cast<int>(item));
|
||||
MetatileSelection selection = editor->metatile_selector_item->getMetatileSelection();
|
||||
for (auto item : selection.metatileItems) {
|
||||
metatiles.append(static_cast<int>(item.metatileId));
|
||||
}
|
||||
OrderedJson::array collisions;
|
||||
for (auto collisionPair : *editor->metatile_selector_item->getSelectedCollisions()) {
|
||||
OrderedJson::object collision;
|
||||
collision["collision"] = collisionPair.first;
|
||||
collision["elevation"] = collisionPair.second;
|
||||
collisions.append(collision);
|
||||
if (selection.hasCollision) {
|
||||
for (auto item : selection.collisionItems) {
|
||||
OrderedJson::object collision;
|
||||
collision["collision"] = item.collision;
|
||||
collision["elevation"] = item.elevation;
|
||||
collisions.append(collision);
|
||||
}
|
||||
}
|
||||
if (collisions.length() != metatiles.length()) {
|
||||
// fill in collisions
|
||||
@@ -1678,6 +1693,8 @@ void MainWindow::on_mapViewTab_tabBarClicked(int index)
|
||||
editor->setEditingMap();
|
||||
} else if (index == 1) {
|
||||
editor->setEditingCollision();
|
||||
} else if (index == 2) {
|
||||
editor->setEditingMap();
|
||||
}
|
||||
editor->setCursorRectVisible(false);
|
||||
}
|
||||
@@ -2875,6 +2892,7 @@ void MainWindow::on_comboBox_PrimaryTileset_currentTextChanged(const QString &ti
|
||||
redrawMapScene();
|
||||
on_horizontalSlider_MetatileZoom_valueChanged(ui->horizontalSlider_MetatileZoom->value());
|
||||
updateTilesetEditor();
|
||||
prefab.updatePrefabUi(editor->map);
|
||||
markMapEdited();
|
||||
}
|
||||
}
|
||||
@@ -2886,6 +2904,7 @@ void MainWindow::on_comboBox_SecondaryTileset_currentTextChanged(const QString &
|
||||
redrawMapScene();
|
||||
on_horizontalSlider_MetatileZoom_valueChanged(ui->horizontalSlider_MetatileZoom->value());
|
||||
updateTilesetEditor();
|
||||
prefab.updatePrefabUi(editor->map);
|
||||
markMapEdited();
|
||||
}
|
||||
}
|
||||
@@ -3008,7 +3027,9 @@ void MainWindow::on_actionTileset_Editor_triggered()
|
||||
} else {
|
||||
this->tilesetEditor->activateWindow();
|
||||
}
|
||||
this->tilesetEditor->selectMetatile(this->editor->metatile_selector_item->getSelectedMetatiles()->at(0));
|
||||
|
||||
MetatileSelection selection = this->editor->metatile_selector_item->getMetatileSelection();
|
||||
this->tilesetEditor->selectMetatile(selection.metatileItems.first().metatileId);
|
||||
}
|
||||
|
||||
void MainWindow::initTilesetEditor() {
|
||||
@@ -3155,6 +3176,15 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() {
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_pushButton_CreatePrefab_clicked() {
|
||||
PrefabCreationDialog dialog(this, this->editor->metatile_selector_item, this->editor->map);
|
||||
dialog.setWindowTitle("Create Prefab");
|
||||
dialog.setWindowModality(Qt::NonModal);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
dialog.savePrefab();
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::initRegionMapEditor() {
|
||||
this->regionMapEditor = new RegionMapEditor(this, this->editor->project);
|
||||
this->regionMapEditor->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
@@ -5,18 +5,17 @@
|
||||
#include <QPainter>
|
||||
|
||||
void BorderMetatilesPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
|
||||
QList<uint16_t> *selectedMetatiles = this->metatileSelector->getSelectedMetatiles();
|
||||
QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions();
|
||||
MetatileSelection selection = this->metatileSelector->getMetatileSelection();
|
||||
QPoint pos = Metatile::coordFromPixmapCoord(event->pos());
|
||||
int width = map->getBorderWidth();
|
||||
int height = map->getBorderHeight();
|
||||
|
||||
Blockdata oldBorder = map->layout->border;
|
||||
|
||||
for (int i = 0; i < selectionDimensions.x() && (i + pos.x()) < width; i++) {
|
||||
for (int j = 0; j < selectionDimensions.y() && (j + pos.y()) < height; j++) {
|
||||
uint16_t metatileId = selectedMetatiles->at(j * selectionDimensions.x() + i);
|
||||
map->setBorderMetatileId(pos.x() + i, pos.y() + j, metatileId, true);
|
||||
for (int i = 0; i < selection.dimensions.x() && (i + pos.x()) < width; i++) {
|
||||
for (int j = 0; j < selection.dimensions.y() && (j + pos.y()) < height; j++) {
|
||||
MetatileSelectionItem item = selection.metatileItems.at(j * selection.dimensions.x() + i);
|
||||
map->setBorderMetatileId(pos.x() + i, pos.y() + j, item.metatileId, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,30 +2,37 @@
|
||||
#include "imageproviders.h"
|
||||
#include <QPainter>
|
||||
|
||||
void CurrentSelectedMetatilesPixmapItem::draw() {
|
||||
QList<uint16_t> *selectedMetatiles = metatileSelector->getSelectedMetatiles();
|
||||
QPoint selectionDimensions = metatileSelector->getSelectionDimensions();
|
||||
int width = selectionDimensions.x() * 16;
|
||||
int height = selectionDimensions.y() * 16;
|
||||
QPixmap drawMetatileSelection(MetatileSelection selection, Map *map) {
|
||||
int width = selection.dimensions.x() * 16;
|
||||
int height = selection.dimensions.y() * 16;
|
||||
QImage image(width, height, QImage::Format_RGBA8888);
|
||||
image.fill(QColor(0, 0, 0, 0));
|
||||
QPainter painter(&image);
|
||||
|
||||
for (int i = 0; i < selectionDimensions.x(); i++) {
|
||||
for (int j = 0; j < selectionDimensions.y(); j++) {
|
||||
for (int i = 0; i < selection.dimensions.x(); i++) {
|
||||
for (int j = 0; j < selection.dimensions.y(); j++) {
|
||||
int x = i * 16;
|
||||
int y = j * 16;
|
||||
int index = j * selectionDimensions.x() + i;
|
||||
QImage metatile_image = getMetatileImage(
|
||||
selectedMetatiles->at(index),
|
||||
map->layout->tileset_primary,
|
||||
map->layout->tileset_secondary,
|
||||
map->metatileLayerOrder,
|
||||
map->metatileLayerOpacity);
|
||||
QPoint metatile_origin = QPoint(x, y);
|
||||
painter.drawImage(metatile_origin, metatile_image);
|
||||
int index = j * selection.dimensions.x() + i;
|
||||
MetatileSelectionItem item = selection.metatileItems.at(index);
|
||||
if (item.enabled) {
|
||||
QImage metatile_image = getMetatileImage(
|
||||
item.metatileId,
|
||||
map->layout->tileset_primary,
|
||||
map->layout->tileset_secondary,
|
||||
map->metatileLayerOrder,
|
||||
map->metatileLayerOpacity);
|
||||
painter.drawImage(metatile_origin, metatile_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
painter.end();
|
||||
setPixmap(QPixmap::fromImage(image));
|
||||
return QPixmap::fromImage(image);
|
||||
}
|
||||
|
||||
void CurrentSelectedMetatilesPixmapItem::draw() {
|
||||
MetatileSelection selection = metatileSelector->getMetatileSelection();
|
||||
setPixmap(drawMetatileSelection(selection, this->map));
|
||||
}
|
||||
|
||||
@@ -102,9 +102,7 @@ void MapPixmapItem::shift(int xDelta, int yDelta, bool fromScriptCall) {
|
||||
}
|
||||
|
||||
void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCall) {
|
||||
QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions();
|
||||
QList<uint16_t> *selectedMetatiles = this->metatileSelector->getSelectedMetatiles();
|
||||
QList<QPair<uint16_t, uint16_t>> *selectedCollisions = this->metatileSelector->getSelectedCollisions();
|
||||
MetatileSelection selection = this->metatileSelector->getMetatileSelection();
|
||||
int initialX = fromScriptCall ? x : this->paint_tile_initial_x;
|
||||
int initialY = fromScriptCall ? y : this->paint_tile_initial_y;
|
||||
|
||||
@@ -112,26 +110,30 @@ void MapPixmapItem::paintNormal(int x, int y, bool fromScriptCall) {
|
||||
// This allows painting via dragging the mouse to tile the painted region.
|
||||
int xDiff = x - initialX;
|
||||
int yDiff = y - initialY;
|
||||
if (xDiff < 0 && xDiff % selectionDimensions.x() != 0) xDiff -= selectionDimensions.x();
|
||||
if (yDiff < 0 && yDiff % selectionDimensions.y() != 0) yDiff -= selectionDimensions.y();
|
||||
if (xDiff < 0 && xDiff % selection.dimensions.x() != 0) xDiff -= selection.dimensions.x();
|
||||
if (yDiff < 0 && yDiff % selection.dimensions.y() != 0) yDiff -= selection.dimensions.y();
|
||||
|
||||
x = initialX + (xDiff / selectionDimensions.x()) * selectionDimensions.x();
|
||||
y = initialY + (yDiff / selectionDimensions.y()) * selectionDimensions.y();
|
||||
x = initialX + (xDiff / selection.dimensions.x()) * selection.dimensions.x();
|
||||
y = initialY + (yDiff / selection.dimensions.y()) * selection.dimensions.y();
|
||||
|
||||
// for edit history
|
||||
Blockdata oldMetatiles = !fromScriptCall ? map->layout->blockdata : Blockdata();
|
||||
|
||||
for (int i = 0; i < selectionDimensions.x() && i + x < map->getWidth(); i++)
|
||||
for (int j = 0; j < selectionDimensions.y() && j + y < map->getHeight(); j++) {
|
||||
for (int i = 0; i < selection.dimensions.x() && i + x < map->getWidth(); i++)
|
||||
for (int j = 0; j < selection.dimensions.y() && j + y < map->getHeight(); j++) {
|
||||
int actualX = i + x;
|
||||
int actualY = j + y;
|
||||
Block block;
|
||||
if (map->getBlock(actualX, actualY, &block)) {
|
||||
int index = j * selectionDimensions.x() + i;
|
||||
block.metatileId = selectedMetatiles->at(index);
|
||||
if (selectedCollisions && selectedCollisions->length() == selectedMetatiles->length()) {
|
||||
block.collision = selectedCollisions->at(index).first;
|
||||
block.elevation = selectedCollisions->at(index).second;
|
||||
int index = j * selection.dimensions.x() + i;
|
||||
MetatileSelectionItem item = selection.metatileItems.at(index);
|
||||
if (!item.enabled)
|
||||
continue;
|
||||
block.metatileId = item.metatileId;
|
||||
if (selection.hasCollision && selection.collisionItems.length() == selection.metatileItems.length()) {
|
||||
CollisionSelectionItem collisionItem = selection.collisionItems.at(index);
|
||||
block.collision = collisionItem.collision;
|
||||
block.elevation = collisionItem.elevation;
|
||||
}
|
||||
map->setBlock(actualX, actualY, block, !fromScriptCall);
|
||||
}
|
||||
@@ -166,22 +168,40 @@ QList<int> MapPixmapItem::smartPathTable = QList<int>({
|
||||
|
||||
#define IS_SMART_PATH_TILE(block) (selectedMetatiles->contains(block.metatileId))
|
||||
|
||||
void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) {
|
||||
QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions();
|
||||
QList<uint16_t> *selectedMetatiles = this->metatileSelector->getSelectedMetatiles();
|
||||
QList<QPair<uint16_t, uint16_t>> *selectedCollisions = this->metatileSelector->getSelectedCollisions();
|
||||
bool isSmartPathTile(QList<MetatileSelectionItem> metatileItems, uint16_t metatileId) {
|
||||
for (int i = 0; i < metatileItems.length(); i++) {
|
||||
if (metatileItems.at(i).metatileId == metatileId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Smart path should never be enabled without a 3x3 block selection.
|
||||
if (selectionDimensions.x() != 3 || selectionDimensions.y() != 3) return;
|
||||
bool isValidSmartPathSelection(MetatileSelection selection) {
|
||||
if (selection.dimensions.x() != 3 || selection.dimensions.y() != 3)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < selection.metatileItems.length(); i++) {
|
||||
if (!selection.metatileItems.at(i).enabled)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) {
|
||||
MetatileSelection selection = this->metatileSelector->getMetatileSelection();
|
||||
if (!isValidSmartPathSelection(selection))
|
||||
return;
|
||||
|
||||
// Shift to the middle tile of the smart path selection.
|
||||
uint16_t openTile = selectedMetatiles->at(4);
|
||||
uint16_t openTile = selection.metatileItems.at(4).metatileId;
|
||||
uint16_t openTileCollision = 0;
|
||||
uint16_t openTileElevation = 0;
|
||||
bool setCollisions = false;
|
||||
if (selectedCollisions && selectedCollisions->length() == selectedMetatiles->length()) {
|
||||
openTileCollision = selectedCollisions->at(4).first;
|
||||
openTileElevation = selectedCollisions->at(4).second;
|
||||
if (selection.hasCollision && selection.collisionItems.length() == selection.metatileItems.length()) {
|
||||
openTileCollision = selection.collisionItems.at(4).collision;
|
||||
openTileElevation = selection.collisionItems.at(4).elevation;
|
||||
setCollisions = true;
|
||||
}
|
||||
|
||||
@@ -220,7 +240,7 @@ void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) {
|
||||
int actualX = i + x;
|
||||
int actualY = j + y;
|
||||
Block block;
|
||||
if (!map->getBlock(actualX, actualY, &block) || !IS_SMART_PATH_TILE(block)) {
|
||||
if (!map->getBlock(actualX, actualY, &block) || !isSmartPathTile(selection.metatileItems, block.metatileId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -231,19 +251,20 @@ void MapPixmapItem::paintSmartPath(int x, int y, bool fromScriptCall) {
|
||||
Block left;
|
||||
|
||||
// Get marching squares value, to determine which tile to use.
|
||||
if (map->getBlock(actualX, actualY - 1, &top) && IS_SMART_PATH_TILE(top))
|
||||
if (map->getBlock(actualX, actualY - 1, &top) && isSmartPathTile(selection.metatileItems, top.metatileId))
|
||||
id += 1;
|
||||
if (map->getBlock(actualX + 1, actualY, &right) && IS_SMART_PATH_TILE(right))
|
||||
if (map->getBlock(actualX + 1, actualY, &right) && isSmartPathTile(selection.metatileItems, right.metatileId))
|
||||
id += 2;
|
||||
if (map->getBlock(actualX, actualY + 1, &bottom) && IS_SMART_PATH_TILE(bottom))
|
||||
if (map->getBlock(actualX, actualY + 1, &bottom) && isSmartPathTile(selection.metatileItems, bottom.metatileId))
|
||||
id += 4;
|
||||
if (map->getBlock(actualX - 1, actualY, &left) && IS_SMART_PATH_TILE(left))
|
||||
if (map->getBlock(actualX - 1, actualY, &left) && isSmartPathTile(selection.metatileItems, left.metatileId))
|
||||
id += 8;
|
||||
|
||||
block.metatileId = selectedMetatiles->at(smartPathTable[id]);
|
||||
block.metatileId = selection.metatileItems.at(smartPathTable[id]).metatileId;
|
||||
if (setCollisions) {
|
||||
block.collision = selectedCollisions->at(smartPathTable[id]).first;
|
||||
block.elevation = selectedCollisions->at(smartPathTable[id]).second;
|
||||
CollisionSelectionItem collisionItem = selection.collisionItems.at(smartPathTable[id]);
|
||||
block.collision = collisionItem.collision;
|
||||
block.elevation = collisionItem.elevation;
|
||||
}
|
||||
map->setBlock(actualX, actualY, block, !fromScriptCall);
|
||||
}
|
||||
@@ -348,12 +369,11 @@ void MapPixmapItem::floodFill(QGraphicsSceneMouseEvent *event) {
|
||||
} else {
|
||||
QPoint pos = Metatile::coordFromPixmapCoord(event->pos());
|
||||
Block block;
|
||||
QList<uint16_t> *selectedMetatiles = this->metatileSelector->getSelectedMetatiles();
|
||||
QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions();
|
||||
int metatileId = selectedMetatiles->first();
|
||||
if (selectedMetatiles->count() > 1 || (map->getBlock(pos.x(), pos.y(), &block) && block.metatileId != metatileId)) {
|
||||
MetatileSelection selection = this->metatileSelector->getMetatileSelection();
|
||||
int metatileId = selection.metatileItems.first().metatileId;
|
||||
if (selection.metatileItems.count() > 1 || (map->getBlock(pos.x(), pos.y(), &block) && block.metatileId != metatileId)) {
|
||||
bool smartPathsEnabled = event->modifiers() & Qt::ShiftModifier;
|
||||
if ((this->settings->smartPathsEnabled || smartPathsEnabled) && selectionDimensions.x() == 3 && selectionDimensions.y() == 3)
|
||||
if ((this->settings->smartPathsEnabled || smartPathsEnabled) && selection.dimensions.x() == 3 && selection.dimensions.y() == 3)
|
||||
this->floodFillSmartPath(pos.x(), pos.y());
|
||||
else
|
||||
this->floodFill(pos.x(), pos.y());
|
||||
@@ -375,34 +395,31 @@ void MapPixmapItem::magicFill(QGraphicsSceneMouseEvent *event) {
|
||||
|
||||
void MapPixmapItem::magicFill(int x, int y, uint16_t metatileId, bool fromScriptCall) {
|
||||
QPoint selectionDimensions(1, 1);
|
||||
QList<uint16_t> *selectedMetatiles = new QList<uint16_t>({ metatileId });
|
||||
this->magicFill(x, y, selectionDimensions, selectedMetatiles, nullptr, fromScriptCall);
|
||||
delete selectedMetatiles;
|
||||
QList<MetatileSelectionItem> selectedMetatiles = QList<MetatileSelectionItem>({MetatileSelectionItem{ true, metatileId }});
|
||||
this->magicFill(x, y, selectionDimensions, selectedMetatiles, QList<CollisionSelectionItem>(), fromScriptCall);
|
||||
}
|
||||
|
||||
void MapPixmapItem::magicFill(int x, int y, bool fromScriptCall) {
|
||||
QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions();
|
||||
QList<uint16_t> *selectedMetatiles = this->metatileSelector->getSelectedMetatiles();
|
||||
QList<QPair<uint16_t, uint16_t>> *selectedCollisions = this->metatileSelector->getSelectedCollisions();
|
||||
this->magicFill(x, y, selectionDimensions, selectedMetatiles, selectedCollisions, fromScriptCall);
|
||||
MetatileSelection selection = this->metatileSelector->getMetatileSelection();
|
||||
this->magicFill(x, y, selection.dimensions, selection.metatileItems, selection.collisionItems, fromScriptCall);
|
||||
}
|
||||
|
||||
void MapPixmapItem::magicFill(
|
||||
int initialX,
|
||||
int initialY,
|
||||
QPoint selectionDimensions,
|
||||
QList<uint16_t> *selectedMetatiles,
|
||||
QList<QPair<uint16_t, uint16_t>> *selectedCollisions,
|
||||
QList<MetatileSelectionItem> selectedMetatiles,
|
||||
QList<CollisionSelectionItem> selectedCollisions,
|
||||
bool fromScriptCall) {
|
||||
Block block;
|
||||
if (map->getBlock(initialX, initialY, &block)) {
|
||||
if (selectedMetatiles->length() == 1 && selectedMetatiles->value(0) == block.metatileId) {
|
||||
if (selectedMetatiles.length() == 1 && selectedMetatiles.at(0).metatileId == block.metatileId) {
|
||||
return;
|
||||
}
|
||||
|
||||
Blockdata oldMetatiles = !fromScriptCall ? map->layout->blockdata : Blockdata();
|
||||
|
||||
bool setCollisions = selectedCollisions && selectedCollisions->length() == selectedMetatiles->length();
|
||||
bool setCollisions = selectedCollisions.length() == selectedMetatiles.length();
|
||||
uint16_t metatileId = block.metatileId;
|
||||
for (int y = 0; y < map->getHeight(); y++) {
|
||||
for (int x = 0; x < map->getWidth(); x++) {
|
||||
@@ -414,12 +431,15 @@ void MapPixmapItem::magicFill(
|
||||
if (i < 0) i = selectionDimensions.x() + i;
|
||||
if (j < 0) j = selectionDimensions.y() + j;
|
||||
int index = j * selectionDimensions.x() + i;
|
||||
block.metatileId = selectedMetatiles->at(index);
|
||||
if (setCollisions) {
|
||||
block.collision = selectedCollisions->at(index).first;
|
||||
block.elevation = selectedCollisions->at(index).second;
|
||||
if (selectedMetatiles.at(index).enabled) {
|
||||
block.metatileId = selectedMetatiles.at(index).metatileId;
|
||||
if (setCollisions) {
|
||||
CollisionSelectionItem item = selectedCollisions.at(index);
|
||||
block.collision = item.collision;
|
||||
block.elevation = item.elevation;
|
||||
}
|
||||
map->setBlock(x, y, block, !fromScriptCall);
|
||||
}
|
||||
map->setBlock(x, y, block, !fromScriptCall);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -431,27 +451,24 @@ void MapPixmapItem::magicFill(
|
||||
}
|
||||
|
||||
void MapPixmapItem::floodFill(int initialX, int initialY, bool fromScriptCall) {
|
||||
QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions();
|
||||
QList<uint16_t> *selectedMetatiles = this->metatileSelector->getSelectedMetatiles();
|
||||
QList<QPair<uint16_t, uint16_t>> *selectedCollisions = this->metatileSelector->getSelectedCollisions();
|
||||
this->floodFill(initialX, initialY, selectionDimensions, selectedMetatiles, selectedCollisions, fromScriptCall);
|
||||
MetatileSelection selection = this->metatileSelector->getMetatileSelection();
|
||||
this->floodFill(initialX, initialY, selection.dimensions, selection.metatileItems, selection.collisionItems, fromScriptCall);
|
||||
}
|
||||
|
||||
void MapPixmapItem::floodFill(int initialX, int initialY, uint16_t metatileId, bool fromScriptCall) {
|
||||
QPoint selectionDimensions(1, 1);
|
||||
QList<uint16_t> *selectedMetatiles = new QList<uint16_t>({ metatileId });
|
||||
this->floodFill(initialX, initialY, selectionDimensions, selectedMetatiles, nullptr, fromScriptCall);
|
||||
delete selectedMetatiles;
|
||||
QList<MetatileSelectionItem> selectedMetatiles = QList<MetatileSelectionItem>({MetatileSelectionItem{true, metatileId}});
|
||||
this->floodFill(initialX, initialY, selectionDimensions, selectedMetatiles, QList<CollisionSelectionItem>(), fromScriptCall);
|
||||
}
|
||||
|
||||
void MapPixmapItem::floodFill(
|
||||
int initialX,
|
||||
int initialY,
|
||||
QPoint selectionDimensions,
|
||||
QList<uint16_t> *selectedMetatiles,
|
||||
QList<QPair<uint16_t, uint16_t>> *selectedCollisions,
|
||||
QList<MetatileSelectionItem> selectedMetatiles,
|
||||
QList<CollisionSelectionItem> selectedCollisions,
|
||||
bool fromScriptCall) {
|
||||
bool setCollisions = selectedCollisions && selectedCollisions->length() == selectedMetatiles->length();
|
||||
bool setCollisions = selectedCollisions.length() == selectedMetatiles.length();
|
||||
Blockdata oldMetatiles = !fromScriptCall ? map->layout->blockdata : Blockdata();
|
||||
|
||||
QSet<int> visited;
|
||||
@@ -474,13 +491,14 @@ void MapPixmapItem::floodFill(
|
||||
if (i < 0) i = selectionDimensions.x() + i;
|
||||
if (j < 0) j = selectionDimensions.y() + j;
|
||||
int index = j * selectionDimensions.x() + i;
|
||||
uint16_t metatileId = selectedMetatiles->at(index);
|
||||
uint16_t metatileId = selectedMetatiles.at(index).metatileId;
|
||||
uint16_t old_metatileId = block.metatileId;
|
||||
if (selectedMetatiles->count() != 1 || old_metatileId != metatileId) {
|
||||
if (selectedMetatiles.at(index).enabled && (selectedMetatiles.count() != 1 || old_metatileId != metatileId)) {
|
||||
block.metatileId = metatileId;
|
||||
if (setCollisions) {
|
||||
block.collision = selectedCollisions->at(index).first;
|
||||
block.elevation = selectedCollisions->at(index).second;
|
||||
CollisionSelectionItem item = selectedCollisions.at(index);
|
||||
block.collision = item.collision;
|
||||
block.elevation = item.elevation;
|
||||
}
|
||||
map->setBlock(x, y, block, !fromScriptCall);
|
||||
}
|
||||
@@ -508,21 +526,19 @@ void MapPixmapItem::floodFill(
|
||||
}
|
||||
|
||||
void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScriptCall) {
|
||||
QPoint selectionDimensions = this->metatileSelector->getSelectionDimensions();
|
||||
QList<uint16_t> *selectedMetatiles = this->metatileSelector->getSelectedMetatiles();
|
||||
QList<QPair<uint16_t, uint16_t>> *selectedCollisions = this->metatileSelector->getSelectedCollisions();
|
||||
|
||||
// Smart path should never be enabled without a 3x3 block selection.
|
||||
if (selectionDimensions.x() != 3 || selectionDimensions.y() != 3) return;
|
||||
MetatileSelection selection = this->metatileSelector->getMetatileSelection();
|
||||
if (!isValidSmartPathSelection(selection))
|
||||
return;
|
||||
|
||||
// Shift to the middle tile of the smart path selection.
|
||||
uint16_t openTile = selectedMetatiles->at(4);
|
||||
uint16_t openTile = selection.metatileItems.at(4).metatileId;
|
||||
uint16_t openTileCollision = 0;
|
||||
uint16_t openTileElevation = 0;
|
||||
bool setCollisions = false;
|
||||
if (selectedCollisions && selectedCollisions->length() == selectedMetatiles->length()) {
|
||||
openTileCollision = selectedCollisions->at(4).first;
|
||||
openTileElevation = selectedCollisions->at(4).second;
|
||||
if (selection.hasCollision && selection.collisionItems.length() == selection.metatileItems.length()) {
|
||||
CollisionSelectionItem item = selection.collisionItems.at(4);
|
||||
openTileCollision = item.collision;
|
||||
openTileElevation = item.elevation;
|
||||
setCollisions = true;
|
||||
}
|
||||
|
||||
@@ -586,36 +602,37 @@ void MapPixmapItem::floodFillSmartPath(int initialX, int initialY, bool fromScri
|
||||
Block left;
|
||||
|
||||
// Get marching squares value, to determine which tile to use.
|
||||
if (map->getBlock(x, y - 1, &top) && IS_SMART_PATH_TILE(top))
|
||||
if (map->getBlock(x, y - 1, &top) && isSmartPathTile(selection.metatileItems, top.metatileId))
|
||||
id += 1;
|
||||
if (map->getBlock(x + 1, y, &right) && IS_SMART_PATH_TILE(right))
|
||||
if (map->getBlock(x + 1, y, &right) && isSmartPathTile(selection.metatileItems, right.metatileId))
|
||||
id += 2;
|
||||
if (map->getBlock(x, y + 1, &bottom) && IS_SMART_PATH_TILE(bottom))
|
||||
if (map->getBlock(x, y + 1, &bottom) && isSmartPathTile(selection.metatileItems, bottom.metatileId))
|
||||
id += 4;
|
||||
if (map->getBlock(x - 1, y, &left) && IS_SMART_PATH_TILE(left))
|
||||
if (map->getBlock(x - 1, y, &left) && isSmartPathTile(selection.metatileItems, left.metatileId))
|
||||
id += 8;
|
||||
|
||||
block.metatileId = selectedMetatiles->at(smartPathTable[id]);
|
||||
block.metatileId = selection.metatileItems.at(smartPathTable[id]).metatileId;
|
||||
if (setCollisions) {
|
||||
block.collision = selectedCollisions->at(smartPathTable[id]).first;
|
||||
block.elevation = selectedCollisions->at(smartPathTable[id]).second;
|
||||
CollisionSelectionItem item = selection.collisionItems.at(smartPathTable[id]);
|
||||
block.collision = item.collision;
|
||||
block.elevation = item.elevation;
|
||||
}
|
||||
map->setBlock(x, y, block, !fromScriptCall);
|
||||
|
||||
// Visit neighbors if they are smart-path tiles, and don't revisit any.
|
||||
if (!visited.contains(x + 1 + y * map->getWidth()) && map->getBlock(x + 1, y, &block) && IS_SMART_PATH_TILE(block)) {
|
||||
if (!visited.contains(x + 1 + y * map->getWidth()) && map->getBlock(x + 1, y, &block) && isSmartPathTile(selection.metatileItems, block.metatileId)) {
|
||||
todo.append(QPoint(x + 1, y));
|
||||
visited.insert(x + 1 + y * map->getWidth());
|
||||
}
|
||||
if (!visited.contains(x - 1 + y * map->getWidth()) && map->getBlock(x - 1, y, &block) && IS_SMART_PATH_TILE(block)) {
|
||||
if (!visited.contains(x - 1 + y * map->getWidth()) && map->getBlock(x - 1, y, &block) && isSmartPathTile(selection.metatileItems, block.metatileId)) {
|
||||
todo.append(QPoint(x - 1, y));
|
||||
visited.insert(x - 1 + y * map->getWidth());
|
||||
}
|
||||
if (!visited.contains(x + (y + 1) * map->getWidth()) && map->getBlock(x, y + 1, &block) && IS_SMART_PATH_TILE(block)) {
|
||||
if (!visited.contains(x + (y + 1) * map->getWidth()) && map->getBlock(x, y + 1, &block) && isSmartPathTile(selection.metatileItems, block.metatileId)) {
|
||||
todo.append(QPoint(x, y + 1));
|
||||
visited.insert(x + (y + 1) * map->getWidth());
|
||||
}
|
||||
if (!visited.contains(x + (y - 1) * map->getWidth()) && map->getBlock(x, y - 1, &block) && IS_SMART_PATH_TILE(block)) {
|
||||
if (!visited.contains(x + (y - 1) * map->getWidth()) && map->getBlock(x, y - 1, &block) && isSmartPathTile(selection.metatileItems, block.metatileId)) {
|
||||
todo.append(QPoint(x, y - 1));
|
||||
visited.insert(x + (y - 1) * map->getWidth());
|
||||
}
|
||||
|
||||
@@ -4,11 +4,7 @@
|
||||
#include <QPainter>
|
||||
|
||||
QPoint MetatileSelector::getSelectionDimensions() {
|
||||
if (this->externalSelection) {
|
||||
return QPoint(this->externalSelectionWidth, this->externalSelectionHeight);
|
||||
} else {
|
||||
return SelectablePixmapItem::getSelectionDimensions();
|
||||
}
|
||||
return selection.dimensions;
|
||||
}
|
||||
|
||||
void MetatileSelector::draw() {
|
||||
@@ -57,7 +53,8 @@ bool MetatileSelector::select(uint16_t metatileId) {
|
||||
bool MetatileSelector::selectFromMap(uint16_t metatileId, uint16_t collision, uint16_t elevation) {
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset)) return false;
|
||||
this->select(metatileId);
|
||||
this->selectedCollisions->append(QPair<uint16_t, uint16_t>(collision, elevation));
|
||||
this->selection.collisionItems.append(CollisionSelectionItem{true, collision, elevation});
|
||||
this->selection.hasCollision = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -71,34 +68,41 @@ void MetatileSelector::setTilesets(Tileset *primaryTileset, Tileset *secondaryTi
|
||||
this->draw();
|
||||
}
|
||||
|
||||
QList<uint16_t>* MetatileSelector::getSelectedMetatiles() {
|
||||
return this->selectedMetatiles;
|
||||
}
|
||||
|
||||
QList<QPair<uint16_t, uint16_t>>* MetatileSelector::getSelectedCollisions() {
|
||||
return this->selectedCollisions;
|
||||
MetatileSelection MetatileSelector::getMetatileSelection() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
void MetatileSelector::setExternalSelection(int width, int height, QList<uint16_t> metatiles, QList<QPair<uint16_t, uint16_t>> collisions) {
|
||||
this->externalSelection = true;
|
||||
this->externalSelectionWidth = width;
|
||||
this->externalSelectionHeight = height;
|
||||
this->externalSelectedMetatiles->clear();
|
||||
this->selectedMetatiles->clear();
|
||||
this->selectedCollisions->clear();
|
||||
this->externalSelectedMetatiles.clear();
|
||||
this->selection.metatileItems.clear();
|
||||
this->selection.collisionItems.clear();
|
||||
this->selection.hasCollision = true;
|
||||
this->selection.dimensions = QPoint(width, height);
|
||||
for (int i = 0; i < metatiles.length(); i++) {
|
||||
this->selectedCollisions->append(collisions.at(i));
|
||||
auto collision = collisions.at(i);
|
||||
this->selection.collisionItems.append(CollisionSelectionItem{true, collision.first, collision.second});
|
||||
uint16_t metatileId = metatiles.at(i);
|
||||
this->externalSelectedMetatiles->append(metatileId);
|
||||
this->externalSelectedMetatiles.append(metatileId);
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
metatileId = 0;
|
||||
this->selectedMetatiles->append(metatileId);
|
||||
this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId});
|
||||
}
|
||||
|
||||
this->draw();
|
||||
emit selectedMetatilesChanged();
|
||||
}
|
||||
|
||||
void MetatileSelector::setDirectSelection(MetatileSelection selection) {
|
||||
this->externalSelection = false;
|
||||
this->externalSelectedMetatiles.clear();
|
||||
this->selection = selection;
|
||||
this->draw();
|
||||
emit selectedMetatilesChanged();
|
||||
}
|
||||
|
||||
bool MetatileSelector::shouldAcceptEvent(QGraphicsSceneMouseEvent *event) {
|
||||
QPoint pos = this->getCellPos(event->pos());
|
||||
return Tileset::metatileIsValid(getMetatileId(pos.x(), pos.y()), this->primaryTileset, this->secondaryTileset);
|
||||
@@ -138,28 +142,30 @@ void MetatileSelector::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
|
||||
|
||||
void MetatileSelector::updateSelectedMetatiles() {
|
||||
this->externalSelection = false;
|
||||
this->selectedMetatiles->clear();
|
||||
this->selectedCollisions->clear();
|
||||
this->selection.metatileItems.clear();
|
||||
this->selection.collisionItems.clear();
|
||||
this->selection.hasCollision = false;
|
||||
this->selection.dimensions = SelectablePixmapItem::getSelectionDimensions();
|
||||
QPoint origin = this->getSelectionStart();
|
||||
QPoint dimensions = this->getSelectionDimensions();
|
||||
for (int j = 0; j < dimensions.y(); j++) {
|
||||
for (int i = 0; i < dimensions.x(); i++) {
|
||||
for (int j = 0; j < this->selection.dimensions.y(); j++) {
|
||||
for (int i = 0; i < this->selection.dimensions.x(); i++) {
|
||||
uint16_t metatileId = this->getMetatileId(origin.x() + i, origin.y() + j);
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
metatileId = 0;
|
||||
this->selectedMetatiles->append(metatileId);
|
||||
this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId});
|
||||
}
|
||||
}
|
||||
emit selectedMetatilesChanged();
|
||||
}
|
||||
|
||||
void MetatileSelector::updateExternalSelectedMetatiles() {
|
||||
this->selectedMetatiles->clear();
|
||||
for (int i = 0; i < this->externalSelectedMetatiles->count(); ++i) {
|
||||
uint16_t metatileId = this->externalSelectedMetatiles->at(i);
|
||||
this->selection.metatileItems.clear();
|
||||
this->selection.dimensions = QPoint(this->externalSelectionWidth, this->externalSelectionHeight);
|
||||
for (int i = 0; i < this->externalSelectedMetatiles.count(); ++i) {
|
||||
uint16_t metatileId = this->externalSelectedMetatiles.at(i);
|
||||
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
|
||||
metatileId = 0;
|
||||
this->selectedMetatiles->append(metatileId);
|
||||
this->selection.metatileItems.append(MetatileSelectionItem{true, metatileId});
|
||||
}
|
||||
emit selectedMetatilesChanged();
|
||||
}
|
||||
|
||||
271
src/ui/prefab.cpp
Normal file
271
src/ui/prefab.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
|
||||
#include "prefab.h"
|
||||
#include "prefabframe.h"
|
||||
#include "ui_prefabframe.h"
|
||||
#include "parseutil.h"
|
||||
#include "currentselectedmetatilespixmapitem.h"
|
||||
#include "log.h"
|
||||
#include "project.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QWidget>
|
||||
#include <QDir>
|
||||
#include <QSpacerItem>
|
||||
#include <QMessageBox>
|
||||
|
||||
using OrderedJson = poryjson::Json;
|
||||
using OrderedJsonDoc = poryjson::JsonDoc;
|
||||
|
||||
void Prefab::loadPrefabs() {
|
||||
this->items.clear();
|
||||
QString filepath = projectConfig.getPrefabFilepath();
|
||||
if (filepath.isEmpty()) return;
|
||||
|
||||
ParseUtil parser;
|
||||
QJsonDocument prefabDoc;
|
||||
QFileInfo info(filepath);
|
||||
if (info.isRelative()) {
|
||||
filepath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath);
|
||||
}
|
||||
if (!QFile::exists(filepath) || !parser.tryParseJsonFile(&prefabDoc, filepath)) {
|
||||
logError(QString("Failed to read prefab data from %1").arg(filepath));
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonArray prefabs = prefabDoc.array();
|
||||
if (prefabs.size() == 0) {
|
||||
logWarn(QString("Prefabs array is empty or missing in %1.").arg(filepath));
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < prefabs.size(); i++) {
|
||||
QJsonObject prefabObj = prefabs[i].toObject();
|
||||
if (prefabObj.isEmpty())
|
||||
continue;
|
||||
|
||||
int width = prefabObj["width"].toInt();
|
||||
int height = prefabObj["height"].toInt();
|
||||
if (width <= 0 || height <= 0)
|
||||
continue;
|
||||
|
||||
QString name = prefabObj["name"].toString();
|
||||
QString primaryTileset = prefabObj["primary_tileset"].toString();
|
||||
QString secondaryTileset = prefabObj["secondary_tileset"].toString();
|
||||
|
||||
MetatileSelection selection;
|
||||
selection.dimensions = QPoint(width, height);
|
||||
selection.hasCollision = true;
|
||||
for (int j = 0; j < width * height; j++) {
|
||||
selection.metatileItems.append(MetatileSelectionItem{false, 0});
|
||||
selection.collisionItems.append(CollisionSelectionItem{false, 0, 0});
|
||||
}
|
||||
QJsonArray metatiles = prefabObj["metatiles"].toArray();
|
||||
for (int j = 0; j < metatiles.size(); j++) {
|
||||
QJsonObject metatileObj = metatiles[j].toObject();
|
||||
int x = metatileObj["x"].toInt();
|
||||
int y = metatileObj["y"].toInt();
|
||||
if (x < 0 || x >= width || y < 0 || y >= height)
|
||||
continue;
|
||||
int index = y * width + x;
|
||||
int metatileId = metatileObj["metatile_id"].toInt();
|
||||
if (metatileId < 0 || metatileId >= Project::getNumMetatilesTotal())
|
||||
continue;
|
||||
selection.metatileItems[index].metatileId = metatileObj["metatile_id"].toInt();
|
||||
selection.collisionItems[index].collision = metatileObj["collision"].toInt();
|
||||
selection.collisionItems[index].elevation = metatileObj["elevation"].toInt();
|
||||
selection.metatileItems[index].enabled = true;
|
||||
selection.collisionItems[index].enabled = true;
|
||||
}
|
||||
|
||||
this->items.append(PrefabItem{QUuid::createUuid(), name, primaryTileset, secondaryTileset, selection});
|
||||
}
|
||||
}
|
||||
|
||||
void Prefab::savePrefabs() {
|
||||
QString filepath = projectConfig.getPrefabFilepath();
|
||||
if (filepath.isEmpty()) return;
|
||||
|
||||
QFileInfo info(filepath);
|
||||
if (info.isRelative()) {
|
||||
filepath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath);
|
||||
}
|
||||
QFile prefabsFile(filepath);
|
||||
if (!prefabsFile.open(QIODevice::WriteOnly)) {
|
||||
logError(QString("Error: Could not open %1 for writing").arg(filepath));
|
||||
QMessageBox messageBox;
|
||||
messageBox.setText("Failed to save prefabs file!");
|
||||
messageBox.setInformativeText(QString("Could not open \"%1\" for writing").arg(filepath));
|
||||
messageBox.setDetailedText("Any created prefabs will not be available next time Porymap is opened. Please fix your prefabs_filepath in the Porymap project config file.");
|
||||
messageBox.setIcon(QMessageBox::Warning);
|
||||
messageBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
OrderedJson::array prefabsArr;
|
||||
for (auto item : this->items) {
|
||||
OrderedJson::object prefabObj;
|
||||
prefabObj["name"] = item.name;
|
||||
prefabObj["width"] = item.selection.dimensions.x();
|
||||
prefabObj["height"] = item.selection.dimensions.y();
|
||||
prefabObj["primary_tileset"] = item.primaryTileset;
|
||||
prefabObj["secondary_tileset"] = item.secondaryTileset;
|
||||
OrderedJson::array metatiles;
|
||||
for (int y = 0; y < item.selection.dimensions.y(); y++) {
|
||||
for (int x = 0; x < item.selection.dimensions.x(); x++) {
|
||||
int index = y * item.selection.dimensions.x() + x;
|
||||
auto metatileItem = item.selection.metatileItems.at(index);
|
||||
if (metatileItem.enabled) {
|
||||
OrderedJson::object metatileObj;
|
||||
metatileObj["x"] = x;
|
||||
metatileObj["y"] = y;
|
||||
metatileObj["metatile_id"] = metatileItem.metatileId;
|
||||
if (item.selection.hasCollision && index < item.selection.collisionItems.size()) {
|
||||
auto collisionItem = item.selection.collisionItems.at(index);
|
||||
metatileObj["collision"] = collisionItem.collision;
|
||||
metatileObj["elevation"] = collisionItem.elevation;
|
||||
}
|
||||
metatiles.push_back(metatileObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
prefabObj["metatiles"] = metatiles;
|
||||
prefabsArr.push_back(prefabObj);
|
||||
}
|
||||
|
||||
OrderedJson prefabJson(prefabsArr);
|
||||
OrderedJsonDoc jsonDoc(&prefabJson);
|
||||
jsonDoc.dump(&prefabsFile);
|
||||
prefabsFile.close();
|
||||
}
|
||||
|
||||
QList<PrefabItem> Prefab::getPrefabsForTilesets(QString primaryTileset, QString secondaryTileset) {
|
||||
QList<PrefabItem> filteredPrefabs;
|
||||
// Prefabs are only valid for the tileset(s) from which they were created.
|
||||
// If, say, no metatiles in the prefab are from the primary tileset, then
|
||||
// any primary tileset is valid for that prefab.
|
||||
for (auto item : this->items) {
|
||||
if ((item.primaryTileset.isEmpty() || item.primaryTileset == primaryTileset) &&
|
||||
(item.secondaryTileset.isEmpty() || item.secondaryTileset == secondaryTileset)) {
|
||||
filteredPrefabs.append(item);
|
||||
}
|
||||
}
|
||||
return filteredPrefabs;
|
||||
}
|
||||
|
||||
void Prefab::initPrefabUI(MetatileSelector *selector, QWidget *prefabWidget, QLabel *emptyPrefabLabel, Map *map) {
|
||||
this->selector = selector;
|
||||
this->prefabWidget = prefabWidget;
|
||||
this->emptyPrefabLabel = emptyPrefabLabel;
|
||||
this->loadPrefabs();
|
||||
this->updatePrefabUi(map);
|
||||
}
|
||||
|
||||
// This function recreates the UI state for the prefab tab.
|
||||
// We completely delete all the prefab widgets, and recreate new widgets
|
||||
// from the relevant list of prefab items.
|
||||
// This is not very efficient, but it gets the job done.
|
||||
void Prefab::updatePrefabUi(Map *map) {
|
||||
if (!this->selector)
|
||||
return;
|
||||
// Cleanup the PrefabFrame to have a clean slate.
|
||||
auto layout = this->prefabWidget->layout();
|
||||
while (layout && layout->count() > 1) {
|
||||
auto child = layout->takeAt(1);
|
||||
if (child->widget()) {
|
||||
delete child->widget();
|
||||
}
|
||||
delete child;
|
||||
}
|
||||
|
||||
QList<PrefabItem> prefabs = this->getPrefabsForTilesets(map->layout->tileset_primary_label, map->layout->tileset_secondary_label);
|
||||
if (prefabs.isEmpty()) {
|
||||
emptyPrefabLabel->setVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
emptyPrefabLabel->setVisible(false);
|
||||
|
||||
// Add all the prefab widgets to this wrapper parent frame because directly adding them
|
||||
// to the main prefab widget will cause the UI to immediately render each new widget, which is slow.
|
||||
// This avoids unpleasant UI lag when a large number of prefabs are present.
|
||||
QFrame *parentFrame = new QFrame();
|
||||
parentFrame->setLayout(new QVBoxLayout());
|
||||
|
||||
for (auto item : prefabs) {
|
||||
PrefabFrame *frame = new PrefabFrame();
|
||||
frame->ui->label_Name->setText(item.name);
|
||||
|
||||
auto scene = new QGraphicsScene;
|
||||
scene->addPixmap(drawMetatileSelection(item.selection, map));
|
||||
scene->setSceneRect(scene->itemsBoundingRect());
|
||||
frame->ui->graphicsView_Prefab->setScene(scene);
|
||||
frame->ui->graphicsView_Prefab->setFixedSize(scene->itemsBoundingRect().width() + 2,
|
||||
scene->itemsBoundingRect().height() + 2);
|
||||
parentFrame->layout()->addWidget(frame);
|
||||
|
||||
// Clicking on the prefab graphics item selects it for painting.
|
||||
QObject::connect(frame->ui->graphicsView_Prefab, &ClickableGraphicsView::clicked, [this, item](){
|
||||
selector->setDirectSelection(item.selection);
|
||||
});
|
||||
|
||||
// Clicking the delete button removes it from the list of known prefabs and updates the UI.
|
||||
QObject::connect(frame->ui->pushButton_DeleteItem, &QPushButton::clicked, [this, item, map](){
|
||||
for (int i = 0; i < this->items.size(); i++) {
|
||||
if (this->items[i].id == item.id) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("Confirm Prefab Deletion");
|
||||
if (this->items[i].name.isEmpty()) {
|
||||
msgBox.setInformativeText("Are you sure you want to delete this prefab?");
|
||||
} else {
|
||||
msgBox.setInformativeText(QString("Are you sure you want to delete prefab \"%1\"?").arg(this->items[i].name));
|
||||
}
|
||||
|
||||
QPushButton *deleteButton = msgBox.addButton("Delete", QMessageBox::DestructiveRole);
|
||||
msgBox.addButton(QMessageBox::Cancel);
|
||||
msgBox.setDefaultButton(QMessageBox::Cancel);
|
||||
msgBox.exec();
|
||||
if (msgBox.clickedButton() == deleteButton) {
|
||||
this->items.removeAt(i);
|
||||
this->savePrefabs();
|
||||
this->updatePrefabUi(map);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
prefabWidget->layout()->addWidget(parentFrame);
|
||||
auto verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding);
|
||||
prefabWidget->layout()->addItem(verticalSpacer);
|
||||
}
|
||||
|
||||
void Prefab::addPrefab(MetatileSelection selection, Map *map, QString name) {
|
||||
// First, determine which tilesets are actually used in this new prefab,
|
||||
// based on the metatile ids.
|
||||
bool usesPrimaryTileset = false;
|
||||
bool usesSecondaryTileset = false;
|
||||
for (auto metatile : selection.metatileItems) {
|
||||
if (!metatile.enabled)
|
||||
continue;
|
||||
if (metatile.metatileId < Project::getNumMetatilesPrimary()) {
|
||||
usesPrimaryTileset = true;
|
||||
} else if (metatile.metatileId < Project::getNumMetatilesTotal()) {
|
||||
usesSecondaryTileset = true;
|
||||
}
|
||||
}
|
||||
|
||||
this->items.append(PrefabItem{
|
||||
QUuid::createUuid(),
|
||||
name,
|
||||
usesPrimaryTileset ? map->layout->tileset_primary_label : "",
|
||||
usesSecondaryTileset ? map->layout->tileset_secondary_label: "",
|
||||
selection
|
||||
});
|
||||
this->savePrefabs();
|
||||
this->updatePrefabUi(map);
|
||||
}
|
||||
|
||||
Prefab prefab;
|
||||
49
src/ui/prefabcreationdialog.cpp
Normal file
49
src/ui/prefabcreationdialog.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "prefabcreationdialog.h"
|
||||
#include "ui_prefabcreationdialog.h"
|
||||
#include "currentselectedmetatilespixmapitem.h"
|
||||
#include "graphicsview.h"
|
||||
#include "prefab.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
PrefabCreationDialog::PrefabCreationDialog(QWidget *parent, MetatileSelector *metatileSelector, Map *map) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PrefabCreationDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
this->map = map;
|
||||
this->selection = metatileSelector->getMetatileSelection();
|
||||
QGraphicsScene *scene = new QGraphicsScene;
|
||||
QGraphicsPixmapItem *pixmapItem = scene->addPixmap(drawMetatileSelection(this->selection, map));
|
||||
scene->setSceneRect(scene->itemsBoundingRect());
|
||||
this->ui->graphicsView_Prefab->setScene(scene);
|
||||
this->ui->graphicsView_Prefab->setFixedSize(scene->itemsBoundingRect().width() + 2,
|
||||
scene->itemsBoundingRect().height() + 2);
|
||||
|
||||
QObject::connect(this->ui->graphicsView_Prefab, &ClickableGraphicsView::clicked, [=](QMouseEvent *event){
|
||||
auto pos = event->pos();
|
||||
int selectionWidth = this->selection.dimensions.x() * 16;
|
||||
int selectionHeight = this->selection.dimensions.y() * 16;
|
||||
if (pos.x() < 0 || pos.x() >= selectionWidth || pos.y() < 0 || pos.y() >= selectionHeight)
|
||||
return;
|
||||
int metatileX = pos.x() / 16;
|
||||
int metatileY = pos.y() / 16;
|
||||
int index = metatileY * this->selection.dimensions.x() + metatileX;
|
||||
bool toggledState = !this->selection.metatileItems[index].enabled;
|
||||
this->selection.metatileItems[index].enabled = toggledState;
|
||||
if (this->selection.hasCollision) {
|
||||
this->selection.collisionItems[index].enabled = toggledState;
|
||||
}
|
||||
pixmapItem->setPixmap(drawMetatileSelection(this->selection, map));
|
||||
});
|
||||
}
|
||||
|
||||
PrefabCreationDialog::~PrefabCreationDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PrefabCreationDialog::savePrefab() {
|
||||
prefab.addPrefab(this->selection, this->map, this->ui->lineEdit_PrefabName->text());
|
||||
}
|
||||
15
src/ui/prefabframe.cpp
Normal file
15
src/ui/prefabframe.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "prefabframe.h"
|
||||
|
||||
#include "ui_prefabframe.h"
|
||||
|
||||
PrefabFrame::PrefabFrame(QWidget *parent) :
|
||||
QFrame(parent),
|
||||
ui(new Ui::PrefabFrame)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
PrefabFrame::~PrefabFrame()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
Reference in New Issue
Block a user