From c4ad0971d0f84ffaf0b5b9bd46426e93e0b28056 Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Sun, 3 Feb 2019 12:26:27 -0600 Subject: [PATCH] Support custom fields for all event types --- include/core/event.h | 5 ++ include/ui/customattributestable.h | 22 ++++++ porymap.pro | 2 + src/core/event.cpp | 108 +++++++++++++++++++++++++++ src/mainwindow.cpp | 6 +- src/project.cpp | 21 ++---- src/ui/customattributestable.cpp | 113 +++++++++++++++++++++++++++++ 7 files changed, 262 insertions(+), 15 deletions(-) create mode 100644 include/ui/customattributestable.h create mode 100644 src/ui/customattributestable.cpp diff --git a/include/core/event.h b/include/core/event.h index 8fbaa80e..af8f2224 100644 --- a/include/core/event.h +++ b/include/core/event.h @@ -23,6 +23,7 @@ class Event { public: Event(); + Event(QJsonObject, QString); public: int x() { return getInt("x"); @@ -75,8 +76,12 @@ public: void setPixmapFromSpritesheet(QImage, int, int); int getPixelX(); int getPixelY(); + QMap getExpectedFields(); + void readCustomValues(QJsonObject values); + void addCustomValuesTo(QJsonObject *obj); QMap values; + QMap customValues; QPixmap pixmap; int spriteWidth; int spriteHeight; diff --git a/include/ui/customattributestable.h b/include/ui/customattributestable.h new file mode 100644 index 00000000..de353b7c --- /dev/null +++ b/include/ui/customattributestable.h @@ -0,0 +1,22 @@ +#ifndef CUSTOMATTRIBUTESTABLE_H +#define CUSTOMATTRIBUTESTABLE_H + +#include "event.h" +#include +#include +#include + +class CustomAttributesTable : public QFrame +{ +public: + explicit CustomAttributesTable(Event *event, QWidget *parent = nullptr); + ~CustomAttributesTable(); + +private: + Event *event; + QTableWidget *table; + void resizeVertically(); + QMap getTableFields(); +}; + +#endif // CUSTOMATTRIBUTESTABLE_H diff --git a/porymap.pro b/porymap.pro index 0611f241..cfd02755 100644 --- a/porymap.pro +++ b/porymap.pro @@ -34,6 +34,7 @@ SOURCES += src/core/block.cpp \ src/ui/connectionpixmapitem.cpp \ src/ui/currentselectedmetatilespixmapitem.cpp \ src/ui/cursortilerect.cpp \ + src/ui/customattributestable.cpp \ src/ui/eventpropertiesframe.cpp \ src/ui/filterchildrenproxymodel.cpp \ src/ui/graphicsview.cpp \ @@ -83,6 +84,7 @@ HEADERS += include/core/block.h \ include/ui/connectionpixmapitem.h \ include/ui/currentselectedmetatilespixmapitem.h \ include/ui/cursortilerect.h \ + include/ui/customattributestable.h \ include/ui/eventpropertiesframe.h \ include/ui/filterchildrenproxymodel.h \ include/ui/graphicsview.h \ diff --git a/src/core/event.cpp b/src/core/event.cpp index 06b8a006..f6a8c4b2 100644 --- a/src/core/event.cpp +++ b/src/core/event.cpp @@ -17,6 +17,13 @@ Event::Event() this->usingSprite = false; } +Event::Event(QJsonObject obj, QString type) +{ + Event(); + this->put("event_type", type); + this->readCustomValues(obj); +} + Event* Event::createNewEvent(QString event_type, QString map_name) { Event *event = new Event; @@ -139,6 +146,100 @@ int Event::getPixelY() return (this->y() * 16) - qMax(0, this->spriteHeight - 16); } +QMap Event::getExpectedFields() +{ + QString type = this->get("event_type"); + if (type == EventType::Object) { + return QMap { + {"graphics_id", true}, + {"x", true}, + {"y", true}, + {"elevation", true}, + {"movement_type", true}, + {"movement_range_x", true}, + {"movement_range_y", true}, + {"trainer_type", true}, + {"trainer_sight_or_berry_tree_id", true}, + {"script", true}, + {"flag", true}, + }; + } else if (type == EventType::Warp) { + return QMap { + {"x", true}, + {"y", true}, + {"elevation", true}, + {"dest_map", true}, + {"dest_warp_id", true}, + }; + } else if (type == EventType::Trigger) { + return QMap { + {"type", true}, + {"x", true}, + {"y", true}, + {"elevation", true}, + {"var", true}, + {"var_value", true}, + {"script", true}, + }; + } else if (type == EventType::WeatherTrigger) { + return QMap { + {"type", true}, + {"x", true}, + {"y", true}, + {"elevation", true}, + {"weather", true}, + }; + } else if (type == EventType::Sign) { + return QMap { + {"type", true}, + {"x", true}, + {"y", true}, + {"elevation", true}, + {"player_facing_dir", true}, + {"script", true}, + }; + } else if (type == EventType::HiddenItem) { + return QMap { + {"type", true}, + {"x", true}, + {"y", true}, + {"elevation", true}, + {"item", true}, + {"flag", true}, + }; + } else if (type == EventType::SecretBase) { + return QMap { + {"type", true}, + {"x", true}, + {"y", true}, + {"elevation", true}, + {"secret_base_id", true}, + }; + } else { + return QMap(); + } +}; + +void Event::readCustomValues(QJsonObject values) +{ + this->customValues.clear(); + QMap expectedValues = this->getExpectedFields(); + for (QString key : values.keys()) { + if (!expectedValues.contains(key)) { + this->customValues[key] = values[key].toString(); + } + } +} + +void Event::addCustomValuesTo(QJsonObject *obj) +{ + for (QString key : this->customValues.keys()) { + if (!obj->contains(key)) { + (*obj)[key] = this->customValues[key]; + } + } +} + QJsonObject Event::buildObjectEventJSON() { QJsonObject eventObj; @@ -153,6 +254,7 @@ QJsonObject Event::buildObjectEventJSON() eventObj["trainer_sight_or_berry_tree_id"] = this->get("sight_radius_tree_id"); eventObj["script"] = this->get("script_label"); eventObj["flag"] = this->get("event_flag"); + this->addCustomValuesTo(&eventObj); return eventObj; } @@ -165,6 +267,7 @@ QJsonObject Event::buildWarpEventJSON(QMap *mapNamesToMapConst warpObj["elevation"] = this->getInt("elevation"); warpObj["dest_map"] = mapNamesToMapConstants->value(this->get("destination_map_name")); warpObj["dest_warp_id"] = this->getInt("destination_warp"); + this->addCustomValuesTo(&warpObj); return warpObj; } @@ -179,6 +282,7 @@ QJsonObject Event::buildTriggerEventJSON() triggerObj["var"] = this->get("script_var"); triggerObj["var_value"] = this->get("script_var_value"); triggerObj["script"] = this->get("script_label"); + this->addCustomValuesTo(&triggerObj); return triggerObj; } @@ -191,6 +295,7 @@ QJsonObject Event::buildWeatherTriggerEventJSON() weatherObj["y"] = this->getU16("y"); weatherObj["elevation"] = this->getInt("elevation"); weatherObj["weather"] = this->get("weather"); + this->addCustomValuesTo(&weatherObj); return weatherObj; } @@ -204,6 +309,7 @@ QJsonObject Event::buildSignEventJSON() signObj["elevation"] = this->getInt("elevation"); signObj["player_facing_dir"] = this->get("player_facing_direction"); signObj["script"] = this->get("script_label"); + this->addCustomValuesTo(&signObj); return signObj; } @@ -217,6 +323,7 @@ QJsonObject Event::buildHiddenItemEventJSON() hiddenItemObj["elevation"] = this->getInt("elevation"); hiddenItemObj["item"] = this->get("item"); hiddenItemObj["flag"] = this->get("flag"); + this->addCustomValuesTo(&hiddenItemObj); return hiddenItemObj; } @@ -229,6 +336,7 @@ QJsonObject Event::buildSecretBaseEventJSON() secretBaseObj["y"] = this->getU16("y"); secretBaseObj["elevation"] = this->getInt("elevation"); secretBaseObj["secret_base_id"] = this->get("secret_base_id"); + this->addCustomValuesTo(&secretBaseObj); return secretBaseObj; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index fdee69ba..c026489b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -8,6 +8,7 @@ #include "ui_eventpropertiesframe.h" #include "bordermetatilespixmapitem.h" #include "currentselectedmetatilespixmapitem.h" +#include "customattributestable.h" #include #include @@ -1376,8 +1377,11 @@ void MainWindow::updateSelectedObjects() { item->bind(combo, key); } - frames.append(frame); + // Custom fields table. + CustomAttributesTable *customAttributes = new CustomAttributesTable(item->event, frame); + frame->layout()->addWidget(customAttributes); + frames.append(frame); } //int scroll = ui->scrollArea_4->verticalScrollBar()->value(); diff --git a/src/project.cpp b/src/project.cpp index 473df657..c62af2c3 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -218,7 +218,7 @@ bool Project::loadMapData(Map* map) { QJsonArray objectEventsArr = mapObj["object_events"].toArray(); for (int i = 0; i < objectEventsArr.size(); i++) { QJsonObject event = objectEventsArr[i].toObject(); - Event *object = new Event; + Event *object = new Event(event, EventType::Object); object->put("map_name", map->name); object->put("sprite", event["graphics_id"].toString()); object->put("x", QString::number(event["x"].toInt())); @@ -232,7 +232,6 @@ bool Project::loadMapData(Map* map) { object->put("script_label", event["script"].toString()); object->put("event_flag", event["flag"].toString()); object->put("event_group_type", "object_event_group"); - object->put("event_type", EventType::Object); map->events["object_event_group"].append(object); } @@ -240,7 +239,7 @@ bool Project::loadMapData(Map* map) { QJsonArray warpEventsArr = mapObj["warp_events"].toArray(); for (int i = 0; i < warpEventsArr.size(); i++) { QJsonObject event = warpEventsArr[i].toObject(); - Event *warp = new Event; + Event *warp = new Event(event, EventType::Warp); warp->put("map_name", map->name); warp->put("x", QString::number(event["x"].toInt())); warp->put("y", QString::number(event["y"].toInt())); @@ -252,12 +251,10 @@ bool Project::loadMapData(Map* map) { if (mapConstantsToMapNames->contains(mapConstant)) { warp->put("destination_map_name", mapConstantsToMapNames->value(mapConstant)); warp->put("event_group_type", "warp_event_group"); - warp->put("event_type", EventType::Warp); map->events["warp_event_group"].append(warp); } else if (mapConstant == NONE_MAP_CONSTANT) { warp->put("destination_map_name", NONE_MAP_NAME); warp->put("event_group_type", "warp_event_group"); - warp->put("event_type", EventType::Warp); map->events["warp_event_group"].append(warp); } else { logError(QString("Destination map constant '%1' is invalid for warp").arg(mapConstant)); @@ -292,7 +289,7 @@ bool Project::loadMapData(Map* map) { QJsonObject event = coordEventsArr[i].toObject(); QString type = event["type"].toString(); if (type == "trigger") { - Event *coord = new Event; + Event *coord = new Event(event, EventType::Trigger); coord->put("map_name", map->name); coord->put("x", QString::number(event["x"].toInt())); coord->put("y", QString::number(event["y"].toInt())); @@ -301,10 +298,9 @@ bool Project::loadMapData(Map* map) { coord->put("script_var_value", QString::number(event["var_value"].toInt())); coord->put("script_label", event["script"].toString()); coord->put("event_group_type", "coord_event_group"); - coord->put("event_type", EventType::Trigger); map->events["coord_event_group"].append(coord); } else if (type == "weather") { - Event *coord = new Event; + Event *coord = new Event(event, EventType::WeatherTrigger); coord->put("map_name", map->name); coord->put("x", QString::number(event["x"].toInt())); coord->put("y", QString::number(event["y"].toInt())); @@ -322,7 +318,7 @@ bool Project::loadMapData(Map* map) { QJsonObject event = bgEventsArr[i].toObject(); QString type = event["type"].toString(); if (type == "sign") { - Event *bg = new Event; + Event *bg = new Event(event, EventType::Sign); bg->put("map_name", map->name); bg->put("x", QString::number(event["x"].toInt())); bg->put("y", QString::number(event["y"].toInt())); @@ -330,10 +326,9 @@ bool Project::loadMapData(Map* map) { bg->put("player_facing_direction", event["player_facing_dir"].toString()); bg->put("script_label", event["script"].toString()); bg->put("event_group_type", "bg_event_group"); - bg->put("event_type", EventType::Sign); map->events["bg_event_group"].append(bg); } else if (type == "hidden_item") { - Event *bg = new Event; + Event *bg = new Event(event, EventType::HiddenItem); bg->put("map_name", map->name); bg->put("x", QString::number(event["x"].toInt())); bg->put("y", QString::number(event["y"].toInt())); @@ -341,17 +336,15 @@ bool Project::loadMapData(Map* map) { bg->put("item", event["item"].toString()); bg->put("flag", event["flag"].toString()); bg->put("event_group_type", "bg_event_group"); - bg->put("event_type", EventType::HiddenItem); map->events["bg_event_group"].append(bg); } else if (type == "secret_base") { - Event *bg = new Event; + Event *bg = new Event(event, EventType::SecretBase); bg->put("map_name", map->name); bg->put("x", QString::number(event["x"].toInt())); bg->put("y", QString::number(event["y"].toInt())); bg->put("elevation", QString::number(event["elevation"].toInt())); bg->put("secret_base_id", event["secret_base_id"].toString()); bg->put("event_group_type", "bg_event_group"); - bg->put("event_type", EventType::SecretBase); map->events["bg_event_group"].append(bg); } } diff --git a/src/ui/customattributestable.cpp b/src/ui/customattributestable.cpp new file mode 100644 index 00000000..b8c2de6c --- /dev/null +++ b/src/ui/customattributestable.cpp @@ -0,0 +1,113 @@ +#include "customattributestable.h" +#include +#include +#include +#include +#include +#include + +CustomAttributesTable::CustomAttributesTable(Event *event, QWidget *parent) : + QFrame(parent) +{ + this->event = event; + + QVBoxLayout *layout = new QVBoxLayout(this); + QLabel *label = new QLabel("Custom Attributes"); + layout->addWidget(label); + + QFrame *buttonsFrame = new QFrame(this); + buttonsFrame->setLayout(new QHBoxLayout()); + QPushButton *addButton = new QPushButton(this); + QPushButton *deleteButton = new QPushButton(this); + addButton->setText("Add"); + deleteButton->setText("Delete"); + buttonsFrame->layout()->addWidget(addButton); + buttonsFrame->layout()->addWidget(deleteButton); + buttonsFrame->layout()->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed)); + buttonsFrame->layout()->setMargin(0); + layout->addWidget(buttonsFrame); + + this->table = new QTableWidget(this); + this->table->setColumnCount(2); + this->table->setHorizontalHeaderLabels(QStringList({"Key", "Value"})); + this->table->horizontalHeader()->setStretchLastSection(true); + layout->addWidget(this->table); + + for (auto it = event->customValues.begin(); it != event->customValues.end(); it++) { + int rowIndex = this->table->rowCount(); + this->table->insertRow(rowIndex); + this->table->setItem(rowIndex, 0, new QTableWidgetItem(it.key())); + this->table->setItem(rowIndex, 1, new QTableWidgetItem(it.value())); + } + + connect(addButton, &QPushButton::clicked, [=]() { + int rowIndex = this->table->rowCount(); + this->table->insertRow(rowIndex); + this->table->selectRow(rowIndex); + this->event->customValues = this->getTableFields(); + this->resizeVertically(); + }); + + connect(deleteButton, &QPushButton::clicked, [=]() { + int rowCount = this->table->rowCount(); + if (rowCount > 0) { + QModelIndexList indexList = this->table->selectionModel()->selectedIndexes(); + QList persistentIndexes; + for (QModelIndex index : indexList) { + QPersistentModelIndex persistentIndex(index); + persistentIndexes.append(persistentIndex); + } + + for (QPersistentModelIndex index : persistentIndexes) { + this->table->removeRow(index.row()); + } + + if (this->table->rowCount() > 0) { + this->table->selectRow(0); + } + + this->event->customValues = this->getTableFields(); + this->resizeVertically(); + } + }); + + connect(this->table, &QTableWidget::cellChanged, [=]() { + this->event->customValues = this->getTableFields(); + }); + + this->resizeVertically(); +} + +CustomAttributesTable::~CustomAttributesTable() +{ +} + +QMap CustomAttributesTable::getTableFields() { + QMap fields; + for (int row = 0; row < table->rowCount(); row++) { + QString keyStr = ""; + QString valueStr = ""; + QTableWidgetItem *key = table->item(row, 0); + QTableWidgetItem *value = table->item(row, 1); + if (key) keyStr = key->text(); + if (value) valueStr = value->text(); + fields[keyStr] = valueStr; + } + return fields; +} + +void CustomAttributesTable::resizeVertically() { + int horizontalHeaderHeight = this->table->horizontalHeader()->height(); + int rowHeight = 0; + for (int i = 0; i < this->table->rowCount(); i++) { + rowHeight += this->table->rowHeight(0); + } + int totalHeight = horizontalHeaderHeight + rowHeight; + if (this->table->rowCount() == 0) { + totalHeight += 1; + } else { + totalHeight += 2; + } + this->table->setMinimumHeight(totalHeight); + this->table->setMaximumHeight(totalHeight); +}