diff --git a/CHANGELOG.md b/CHANGELOG.md index f536df56..da41ed29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d ### Changed - Overhauled the region map editor, adding support for tilemaps, and significant customization. Also now supports pokefirered. +- The Custom Attributes table for map headers and events now supports types other than strings. - If an object event is inanimate, it will always render using its first frame. - Unused metatile attribute bits are preserved instead of being cleared. - The wild encounter editor is automatically disabled if the encounter JSON data cannot be read @@ -42,6 +43,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d - The number and order of entries in the heal location data tables can now be changed arbitrarily, and independently of each other. - The metatile behavior is now displayed in the bottom bar mouseover text. - Removed some unnecessary error logs from the scripting API and added new useful ones. +- If any JSON data is the incorrect type Porymap will now attempt to convert it. ### Fixed - Fix events losing their assigned script when the script autocomplete is used. diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index 2e1ddda6..52772b99 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -2276,14 +2276,14 @@ - + Allow Dig & Escape Rope - + <html><head/><body><p>Allows the player to use Dig or Escape Rope</p></body></html> @@ -2388,7 +2388,7 @@ Custom fields will be added to the map.json file for the current map. - false + true false @@ -2405,6 +2405,11 @@ false + + + Type + + Key diff --git a/include/core/events.h b/include/core/events.h index 912fe0c7..ba437c6c 100644 --- a/include/core/events.h +++ b/include/core/events.h @@ -151,8 +151,8 @@ public: virtual QSet getExpectedFields() = 0; void readCustomValues(QJsonObject values); void addCustomValuesTo(OrderedJson::object *obj); - QMap getCustomValues() { return this->customValues; } - void setCustomValues(QMap newCustomValues) { this->customValues = newCustomValues; } + const QMap getCustomValues() { return this->customValues; } + void setCustomValues(const QMap newCustomValues) { this->customValues = newCustomValues; } virtual void loadPixmap(Project *project) = 0; @@ -173,6 +173,7 @@ public: int getEventIndex(); + static QString eventGroupToString(Event::Group group); static QString eventTypeToString(Event::Type type); static Event::Type eventTypeFromString(QString type); @@ -192,7 +193,7 @@ protected: int spriteHeight = 16; bool usingSprite = false; - QMap customValues; + QMap customValues; QPixmap pixmap; DraggablePixmapItem *pixmapItem = nullptr; @@ -340,12 +341,12 @@ public: void setDestinationMap(QString newDestinationMap) { this->destinationMap = newDestinationMap; } QString getDestinationMap() { return this->destinationMap; } - void setDestinationWarpID(int newDestinationWarpID) { this->destinationWarpID = newDestinationWarpID; } - int getDestinationWarpID() { return this->destinationWarpID; } + void setDestinationWarpID(QString newDestinationWarpID) { this->destinationWarpID = newDestinationWarpID; } + QString getDestinationWarpID() { return this->destinationWarpID; } private: QString destinationMap; - int destinationWarpID = 0; + QString destinationWarpID; }; diff --git a/include/core/map.h b/include/core/map.h index 2b10e6cf..24bcfd9e 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -42,23 +42,23 @@ public: QString song; QString layoutId; QString location; - QString requiresFlash; - QString isFlyable; + bool requiresFlash; QString weather; QString type; - QString show_location; - QString allowRunning; - QString allowBiking; - QString allowEscapeRope; + bool show_location; + bool allowRunning; + bool allowBiking; + bool allowEscaping; int floorNumber = 0; QString battle_scene; QString sharedEventsMap = ""; QString sharedScriptsMap = ""; - QMap customHeaders; + QMap customHeaders; MapLayout *layout; bool isPersistedToFile = true; bool hasUnsavedDataChanges = false; bool needsLayoutDir = true; + bool needsHealLocation = false; QImage collision_image; QPixmap collision_pixmap; QImage image; diff --git a/include/core/mapconnection.h b/include/core/mapconnection.h index b6be238e..11aa0e59 100644 --- a/include/core/mapconnection.h +++ b/include/core/mapconnection.h @@ -8,7 +8,7 @@ class MapConnection { public: QString direction; - QString offset; + int offset; QString map_name; }; diff --git a/include/core/maplayout.h b/include/core/maplayout.h index 888e5372..13215fff 100644 --- a/include/core/maplayout.h +++ b/include/core/maplayout.h @@ -14,10 +14,10 @@ public: static QString layoutConstantFromName(QString mapName); QString id; QString name; - QString width; - QString height; - QString border_width; - QString border_height; + int width; + int height; + int border_width; + int border_height; QString border_path; QString blockdata_path; QString tileset_primary_label; diff --git a/include/core/parseutil.h b/include/core/parseutil.h index 97bf2e71..298325ee 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -72,7 +72,11 @@ public: static QString removeLineComments(QString text, const QStringList &commentSymbols); static QStringList splitShellCommand(QStringView command); + static int gameStringToInt(QString gameString, bool * ok = nullptr); static bool gameStringToBool(QString gameString, bool * ok = nullptr); + static QString jsonToQString(QJsonValue value, bool * ok = nullptr); + static int jsonToInt(QJsonValue value, bool * ok = nullptr); + static bool jsonToBool(QJsonValue value, bool * ok = nullptr); private: QString root; diff --git a/include/lib/orderedjson.h b/include/lib/orderedjson.h index 17dafe10..c13ec13c 100644 --- a/include/lib/orderedjson.h +++ b/include/lib/orderedjson.h @@ -60,6 +60,9 @@ #include #include #include +#include +#include +#include #include #include @@ -129,6 +132,8 @@ public: int>::type = 0> Json(const V & v) : Json(array(v.begin(), v.end())) {} + static const Json fromQJsonValue(QJsonValue value); + // This prevents Json(some_pointer) from accidentally producing a bool. Use // Json(bool(some_pointer)) if that behavior is desired. Json(void *) = delete; diff --git a/include/mainwindow.h b/include/mainwindow.h index 84290f7a..b8653d58 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -196,7 +196,7 @@ private slots: void on_checkBox_ShowLocation_stateChanged(int selected); void on_checkBox_AllowRunning_stateChanged(int selected); void on_checkBox_AllowBiking_stateChanged(int selected); - void on_checkBox_AllowEscapeRope_stateChanged(int selected); + void on_checkBox_AllowEscaping_stateChanged(int selected); void on_spinBox_FloorNumber_valueChanged(int offset); void on_actionUse_Encounter_Json_triggered(bool checked); void on_actionMonitor_Project_Files_triggered(bool checked); @@ -219,6 +219,7 @@ private slots: void on_toolButton_deleteObject_clicked(); void addNewEvent(Event::Type type); + void tryAddEventTab(QWidget * tab, Event::Group group); void displayEventTabs(); void updateSelectedObjects(); void updateObjects(); @@ -387,6 +388,7 @@ private: void redrawMetatileSelection(); QObjectList shortcutableObjects() const; + void addCustomHeaderValue(QString key, QJsonValue value, bool isNew = false); }; enum MapListUserRoles { diff --git a/include/project.h b/include/project.h index bc42ddb5..28c3a296 100644 --- a/include/project.h +++ b/include/project.h @@ -26,8 +26,9 @@ struct EventGraphics bool inanimate; }; -static QString NONE_MAP_CONSTANT = "MAP_NONE"; -static QString NONE_MAP_NAME = "None"; +// The constant and displayed name of the special map value used by warps with multiple potential destinations +static QString DYNAMIC_MAP_CONSTANT = "MAP_DYNAMIC"; +static QString DYNAMIC_MAP_NAME = "Dynamic"; class Project : public QObject { @@ -136,7 +137,7 @@ public: bool readSpeciesIconPaths(); QMap speciesToIconPath; - QMap getTopLevelMapFields(); + QSet getTopLevelMapFields(); bool loadMapData(Map*); bool readMapLayouts(); bool loadLayout(MapLayout *); diff --git a/include/ui/connectionpixmapitem.h b/include/ui/connectionpixmapitem.h index a4cdf4e2..e12296af 100644 --- a/include/ui/connectionpixmapitem.h +++ b/include/ui/connectionpixmapitem.h @@ -15,7 +15,7 @@ public: setFlag(ItemSendsGeometryChanges); this->initialX = x; this->initialY = y; - this->initialOffset = connection->offset.toInt(); + this->initialOffset = connection->offset; this->baseMapWidth = baseMapWidth; this->baseMapHeight = baseMapHeight; } diff --git a/include/ui/customattributestable.h b/include/ui/customattributestable.h index bf9334d0..f17f4877 100644 --- a/include/ui/customattributestable.h +++ b/include/ui/customattributestable.h @@ -12,11 +12,15 @@ public: explicit CustomAttributesTable(Event *event, QWidget *parent = nullptr); ~CustomAttributesTable(); + static const QMap getAttributes(QTableWidget * table); + static QJsonValue pickType(QWidget * parent, bool * ok = nullptr); + static void addAttribute(QTableWidget * table, QString key, QJsonValue value, bool isNew = false); + static bool deleteSelectedAttributes(QTableWidget * table); + private: Event *event; QTableWidget *table; void resizeVertically(); - QMap getTableFields(); }; #endif // CUSTOMATTRIBUTESTABLE_H diff --git a/include/ui/eventframes.h b/include/ui/eventframes.h index 181ac141..a85a8f08 100644 --- a/include/ui/eventframes.h +++ b/include/ui/eventframes.h @@ -133,7 +133,7 @@ public: public: NoScrollComboBox *combo_dest_map; - NoScrollSpinBox *spinner_dest_warp; + NoScrollComboBox *combo_dest_warp; private: WarpEvent *warp; diff --git a/src/core/events.cpp b/src/core/events.cpp index 9f08174f..bf67c566 100644 --- a/src/core/events.cpp +++ b/src/core/events.cpp @@ -43,7 +43,7 @@ void Event::readCustomValues(QJsonObject values) { QSet expectedFields = this->getExpectedFields(); for (QString key : values.keys()) { if (!expectedFields.contains(key)) { - this->customValues[key] = values[key].toString(); + this->customValues[key] = values[key]; } } } @@ -51,7 +51,7 @@ void Event::readCustomValues(QJsonObject values) { void Event::addCustomValuesTo(OrderedJson::object *obj) { for (QString key : this->customValues.keys()) { if (!obj->contains(key)) { - (*obj)[key] = this->customValues[key]; + (*obj)[key] = OrderedJson::fromQJsonValue(this->customValues[key]); } } } @@ -60,6 +60,23 @@ void Event::modify() { this->map->modify(); } +QString Event::eventGroupToString(Event::Group group) { + switch (group) { + case Event::Group::Object: + return "Object"; + case Event::Group::Warp: + return "Warp"; + case Event::Group::Coord: + return "Trigger"; + case Event::Group::Bg: + return "BG"; + case Event::Group::Heal: + return "Healspot"; + default: + return ""; + } +} + QString Event::eventTypeToString(Event::Type type) { switch (type) { case Event::Type::Object: @@ -163,17 +180,17 @@ OrderedJson::object ObjectEvent::buildEventJson(Project *) { } bool ObjectEvent::loadFromJson(QJsonObject json, Project *) { - this->setX(json["x"].toInt()); - this->setY(json["y"].toInt()); - this->setElevation(json["elevation"].toInt()); - this->setGfx(json["graphics_id"].toString()); - this->setMovement(json["movement_type"].toString()); - this->setRadiusX(json["movement_range_x"].toInt()); - this->setRadiusY(json["movement_range_y"].toInt()); - this->setTrainerType(json["trainer_type"].toString()); - this->setSightRadiusBerryTreeID(json["trainer_sight_or_berry_tree_id"].toString()); - this->setScript(json["script"].toString()); - this->setFlag(json["flag"].toString()); + this->setX(ParseUtil::jsonToInt(json["x"])); + this->setY(ParseUtil::jsonToInt(json["y"])); + this->setElevation(ParseUtil::jsonToInt(json["elevation"])); + this->setGfx(ParseUtil::jsonToQString(json["graphics_id"])); + this->setMovement(ParseUtil::jsonToQString(json["movement_type"])); + this->setRadiusX(ParseUtil::jsonToInt(json["movement_range_x"])); + this->setRadiusY(ParseUtil::jsonToInt(json["movement_range_y"])); + this->setTrainerType(ParseUtil::jsonToQString(json["trainer_type"])); + this->setSightRadiusBerryTreeID(ParseUtil::jsonToQString(json["trainer_sight_or_berry_tree_id"])); + this->setScript(ParseUtil::jsonToQString(json["script"])); + this->setFlag(ParseUtil::jsonToQString(json["flag"])); this->readCustomValues(json); @@ -220,7 +237,7 @@ void ObjectEvent::loadPixmap(Project *project) { // Invalid gfx constant. // If this is a number, try to use that instead. bool ok; - int altGfx = this->gfx.toInt(&ok); + int altGfx = ParseUtil::gameStringToInt(this->gfx, &ok); if (ok && (altGfx < project->gfxDefines.count())) { eventGfx = project->eventGraphicsMap.value(project->gfxDefines.key(altGfx, "NULL"), nullptr); } @@ -311,20 +328,20 @@ OrderedJson::object CloneObjectEvent::buildEventJson(Project *project) { } bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) { - this->setX(json["x"].toInt()); - this->setY(json["y"].toInt()); - this->setGfx(json["graphics_id"].toString()); - this->setTargetID(json["target_local_id"].toInt()); + this->setX(ParseUtil::jsonToInt(json["x"])); + this->setY(ParseUtil::jsonToInt(json["y"])); + this->setGfx(ParseUtil::jsonToQString(json["graphics_id"])); + this->setTargetID(ParseUtil::jsonToInt(json["target_local_id"])); // Ensure the target map constant is valid before adding it to the events. - QString mapConstant = json["target_map"].toString(); + QString mapConstant = ParseUtil::jsonToQString(json["target_map"]); if (project->mapConstantsToMapNames.contains(mapConstant)) { this->setTargetMap(project->mapConstantsToMapNames.value(mapConstant)); - } else if (mapConstant == NONE_MAP_CONSTANT) { - this->setTargetMap(NONE_MAP_NAME); + } else if (mapConstant == DYNAMIC_MAP_CONSTANT) { + this->setTargetMap(DYNAMIC_MAP_NAME); } else { - logError(QString("Destination map constant '%1' is invalid").arg(mapConstant)); - return false; + logWarn(QString("Target Map constant '%1' is invalid. Using default '%2'.").arg(mapConstant).arg(DYNAMIC_MAP_CONSTANT)); + this->setTargetMap(DYNAMIC_MAP_NAME); } this->readCustomValues(json); @@ -421,20 +438,20 @@ OrderedJson::object WarpEvent::buildEventJson(Project *project) { } bool WarpEvent::loadFromJson(QJsonObject json, Project *project) { - this->setX(json["x"].toInt()); - this->setY(json["y"].toInt()); - this->setElevation(json["elevation"].toInt()); - this->setDestinationWarpID(json["dest_warp_id"].toInt()); + this->setX(ParseUtil::jsonToInt(json["x"])); + this->setY(ParseUtil::jsonToInt(json["y"])); + this->setElevation(ParseUtil::jsonToInt(json["elevation"])); + this->setDestinationWarpID(ParseUtil::jsonToQString(json["dest_warp_id"])); // Ensure the warp destination map constant is valid before adding it to the warps. - QString mapConstant = json["dest_map"].toString(); + QString mapConstant = ParseUtil::jsonToQString(json["dest_map"]); if (project->mapConstantsToMapNames.contains(mapConstant)) { this->setDestinationMap(project->mapConstantsToMapNames.value(mapConstant)); - } else if (mapConstant == NONE_MAP_CONSTANT) { - this->setDestinationMap(NONE_MAP_NAME); + } else if (mapConstant == DYNAMIC_MAP_CONSTANT) { + this->setDestinationMap(DYNAMIC_MAP_NAME); } else { - logError(QString("Destination map constant '%1' is invalid for warp").arg(mapConstant)); - return false; + logWarn(QString("Destination Map constant '%1' is invalid. Using default '%2'.").arg(mapConstant).arg(DYNAMIC_MAP_CONSTANT)); + this->setDestinationMap(DYNAMIC_MAP_NAME); } this->readCustomValues(json); @@ -444,7 +461,7 @@ bool WarpEvent::loadFromJson(QJsonObject json, Project *project) { void WarpEvent::setDefaultValues(Project *) { if (this->getMap()) this->setDestinationMap(this->getMap()->name); - this->setDestinationWarpID(0); + this->setDestinationWarpID("0"); this->setElevation(0); } @@ -515,12 +532,12 @@ OrderedJson::object TriggerEvent::buildEventJson(Project *) { } bool TriggerEvent::loadFromJson(QJsonObject json, Project *) { - this->setX(json["x"].toInt()); - this->setY(json["y"].toInt()); - this->setElevation(json["elevation"].toInt()); - this->setScriptVar(json["var"].toString()); - this->setScriptVarValue(json["var_value"].toString()); - this->setScriptLabel(json["script"].toString()); + this->setX(ParseUtil::jsonToInt(json["x"])); + this->setY(ParseUtil::jsonToInt(json["y"])); + this->setElevation(ParseUtil::jsonToInt(json["elevation"])); + this->setScriptVar(ParseUtil::jsonToQString(json["var"])); + this->setScriptVarValue(ParseUtil::jsonToQString(json["var_value"])); + this->setScriptLabel(ParseUtil::jsonToQString(json["script"])); this->readCustomValues(json); @@ -589,10 +606,10 @@ OrderedJson::object WeatherTriggerEvent::buildEventJson(Project *) { } bool WeatherTriggerEvent::loadFromJson(QJsonObject json, Project *) { - this->setX(json["x"].toInt()); - this->setY(json["y"].toInt()); - this->setElevation(json["elevation"].toInt()); - this->setWeather(json["weather"].toString()); + this->setX(ParseUtil::jsonToInt(json["x"])); + this->setY(ParseUtil::jsonToInt(json["y"])); + this->setElevation(ParseUtil::jsonToInt(json["elevation"])); + this->setWeather(ParseUtil::jsonToQString(json["weather"])); this->readCustomValues(json); @@ -665,11 +682,11 @@ OrderedJson::object SignEvent::buildEventJson(Project *) { } bool SignEvent::loadFromJson(QJsonObject json, Project *) { - this->setX(json["x"].toInt()); - this->setY(json["y"].toInt()); - this->setElevation(json["elevation"].toInt()); - this->setFacingDirection(json["player_facing_dir"].toString()); - this->setScriptLabel(json["script"].toString()); + this->setX(ParseUtil::jsonToInt(json["x"])); + this->setY(ParseUtil::jsonToInt(json["y"])); + this->setElevation(ParseUtil::jsonToInt(json["elevation"])); + this->setFacingDirection(ParseUtil::jsonToQString(json["player_facing_dir"])); + this->setScriptLabel(ParseUtil::jsonToQString(json["script"])); this->readCustomValues(json); @@ -746,16 +763,16 @@ OrderedJson::object HiddenItemEvent::buildEventJson(Project *) { } bool HiddenItemEvent::loadFromJson(QJsonObject json, Project *) { - this->setX(json["x"].toInt()); - this->setY(json["y"].toInt()); - this->setElevation(json["elevation"].toInt()); - this->setItem(json["item"].toString()); - this->setFlag(json["flag"].toString()); + this->setX(ParseUtil::jsonToInt(json["x"])); + this->setY(ParseUtil::jsonToInt(json["y"])); + this->setElevation(ParseUtil::jsonToInt(json["elevation"])); + this->setItem(ParseUtil::jsonToQString(json["item"])); + this->setFlag(ParseUtil::jsonToQString(json["flag"])); if (projectConfig.getHiddenItemQuantityEnabled()) { - this->setQuantity(json["quantity"].toInt()); + this->setQuantity(ParseUtil::jsonToInt(json["quantity"])); } if (projectConfig.getHiddenItemRequiresItemfinderEnabled()) { - this->setUnderfoot(json["underfoot"].toBool()); + this->setUnderfoot(ParseUtil::jsonToBool(json["underfoot"])); } this->readCustomValues(json); @@ -834,10 +851,10 @@ OrderedJson::object SecretBaseEvent::buildEventJson(Project *) { } bool SecretBaseEvent::loadFromJson(QJsonObject json, Project *) { - this->setX(json["x"].toInt()); - this->setY(json["y"].toInt()); - this->setElevation(json["elevation"].toInt()); - this->setBaseID(json["secret_base_id"].toString()); + this->setX(ParseUtil::jsonToInt(json["x"])); + this->setY(ParseUtil::jsonToInt(json["y"])); + this->setElevation(ParseUtil::jsonToInt(json["elevation"])); + this->setBaseID(ParseUtil::jsonToQString(json["secret_base_id"])); this->readCustomValues(json); diff --git a/src/core/map.cpp b/src/core/map.cpp index e12793b8..8080682e 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -310,10 +310,10 @@ void Map::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool setNewDimensionsBlockdata(newWidth, newHeight); } - int oldWidth = layout->width.toInt(); - int oldHeight = layout->height.toInt(); - layout->width = QString::number(newWidth); - layout->height = QString::number(newHeight); + int oldWidth = layout->width; + int oldHeight = layout->height; + layout->width = newWidth; + layout->height = newHeight; if (enableScriptCallback && (oldWidth != newWidth || oldHeight != newHeight)) { Scripting::cb_MapResized(oldWidth, oldHeight, newWidth, newHeight); @@ -328,10 +328,10 @@ void Map::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata, setNewBorderDimensionsBlockdata(newWidth, newHeight); } - int oldWidth = layout->border_width.toInt(); - int oldHeight = layout->border_height.toInt(); - layout->border_width = QString::number(newWidth); - layout->border_height = QString::number(newHeight); + int oldWidth = layout->border_width; + int oldHeight = layout->border_height; + layout->border_width = newWidth; + layout->border_height = newHeight; if (enableScriptCallback && (oldWidth != newWidth || oldHeight != newHeight)) { Scripting::cb_BorderResized(oldWidth, oldHeight, newWidth, newHeight); diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp index 8b1a1d3f..310017be 100644 --- a/src/core/maplayout.cpp +++ b/src/core/maplayout.cpp @@ -16,17 +16,17 @@ QString MapLayout::layoutConstantFromName(QString mapName) { } int MapLayout::getWidth() { - return width.toInt(nullptr, 0); + return width; } int MapLayout::getHeight() { - return height.toInt(nullptr, 0); + return height; } int MapLayout::getBorderWidth() { - return border_width.toInt(nullptr, 0); + return border_width; } int MapLayout::getBorderHeight() { - return border_height.toInt(nullptr, 0); + return border_height; } diff --git a/src/core/mapparser.cpp b/src/core/mapparser.cpp index 88592ab0..986c2c24 100644 --- a/src/core/mapparser.cpp +++ b/src/core/mapparser.cpp @@ -70,10 +70,10 @@ MapLayout *MapParser::parse(QString filepath, bool *error, Project *project) } MapLayout *mapLayout = new MapLayout(); - mapLayout->width = QString::number(mapWidth); - mapLayout->height = QString::number(mapHeight); - mapLayout->border_width = (borderWidth == 0) ? QString::number(2) : QString::number(borderWidth); - mapLayout->border_height = (borderHeight == 0) ? QString::number(2) : QString::number(borderHeight); + mapLayout->width = mapWidth; + mapLayout->height = mapHeight; + mapLayout->border_width = (borderWidth == 0) ? DEFAULT_BORDER_WIDTH : borderWidth; + mapLayout->border_height = (borderHeight == 0) ? DEFAULT_BORDER_HEIGHT : borderHeight; QList tilesets = project->tilesetLabelsOrdered; diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index 9c2be360..1f933e7c 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -404,13 +404,17 @@ QMap ParseUtil::readNamedIndexCArray(const QString &filename, return map; } -bool ParseUtil::gameStringToBool(QString gameString, bool * ok) { +int ParseUtil::gameStringToInt(QString gameString, bool * ok) { if (ok) *ok = true; if (QString::compare(gameString, "TRUE", Qt::CaseInsensitive) == 0) - return true; + return 1; if (QString::compare(gameString, "FALSE", Qt::CaseInsensitive) == 0) - return false; - return gameString.toInt(ok) != 0; + return 0; + return gameString.toInt(ok); +} + +bool ParseUtil::gameStringToBool(QString gameString, bool * ok) { + return gameStringToInt(gameString, ok) != 0; } QMap> ParseUtil::readCStructs(const QString &filename, const QString &label, const QHash memberMap) { @@ -522,6 +526,48 @@ bool ParseUtil::ensureFieldsExist(const QJsonObject &obj, const QList & return true; } +// QJsonValues are strictly typed, and so will not attempt any implicit conversions. +// The below functions are for attempting to convert a JSON value read from the user's +// project to a QString, int, or bool (whichever Porymap expects). +QString ParseUtil::jsonToQString(QJsonValue value, bool * ok) { + if (ok) *ok = true; + switch (value.type()) + { + case QJsonValue::String: return value.toString(); + case QJsonValue::Double: return QString::number(value.toInt()); + case QJsonValue::Bool: return QString::number(value.toBool()); + default: break; + } + if (ok) *ok = false; + return QString(); +} + +int ParseUtil::jsonToInt(QJsonValue value, bool * ok) { + if (ok) *ok = true; + switch (value.type()) + { + case QJsonValue::String: return ParseUtil::gameStringToInt(value.toString(), ok); + case QJsonValue::Double: return value.toInt(); + case QJsonValue::Bool: return value.toBool(); + default: break; + } + if (ok) *ok = false; + return 0; +} + +bool ParseUtil::jsonToBool(QJsonValue value, bool * ok) { + if (ok) *ok = true; + switch (value.type()) + { + case QJsonValue::String: return ParseUtil::gameStringToBool(value.toString(), ok); + case QJsonValue::Double: return value.toInt() != 0; + case QJsonValue::Bool: return value.toBool(); + default: break; + } + if (ok) *ok = false; + return false; +} + int ParseUtil::getScriptLineNumber(const QString &filePath, const QString &scriptLabel) { if (scriptLabel.isEmpty()) return 0; diff --git a/src/editor.cpp b/src/editor.cpp index cdd1619f..77380aab 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -10,6 +10,7 @@ #include "editcommands.h" #include "config.h" #include "scripting.h" +#include "customattributestable.h" #include #include #include @@ -176,7 +177,7 @@ void Editor::setEditingConnections() { bool controlsEnabled = selected_connection_item != nullptr; setConnectionEditControlsEnabled(controlsEnabled); if (selected_connection_item) { - onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt()); + onConnectionOffsetChanged(selected_connection_item->connection->offset); setConnectionMap(selected_connection_item->connection->map_name); setCurrentConnectionDirection(selected_connection_item->connection->direction); } @@ -774,7 +775,7 @@ void Editor::setCurrentConnectionDirection(QString curDirection) { selected_connection_item->connection->direction = curDirection; QPixmap pixmap = connected_map->renderConnection(*selected_connection_item->connection, map->layout); - int offset = selected_connection_item->connection->offset.toInt(nullptr, 0); + int offset = selected_connection_item->connection->offset; selected_connection_item->initialOffset = offset; int x = 0, y = 0; if (selected_connection_item->connection->direction == "up") { @@ -820,7 +821,7 @@ void Editor::updateCurrentConnectionDirection(QString curDirection) { void Editor::onConnectionMoved(MapConnection* connection) { updateMirroredConnectionOffset(connection); - onConnectionOffsetChanged(connection->offset.toInt()); + onConnectionOffsetChanged(connection->offset); maskNonVisibleConnectionTiles(); } @@ -834,7 +835,7 @@ void Editor::onConnectionOffsetChanged(int newOffset) { void Editor::setConnectionEditControlValues(MapConnection* connection) { QString mapName = connection ? connection->map_name : ""; QString direction = connection ? connection->direction : ""; - int offset = connection ? connection->offset.toInt() : 0; + int offset = connection ? connection->offset : 0; ui->comboBox_ConnectedMap->blockSignals(true); ui->comboBox_ConnectionDirection->blockSignals(true); @@ -883,7 +884,7 @@ void Editor::onConnectionItemSelected(ConnectionPixmapItem* connectionItem) { setConnectionEditControlValues(selected_connection_item->connection); ui->spinBox_ConnectionOffset->setMaximum(selected_connection_item->getMaxOffset()); ui->spinBox_ConnectionOffset->setMinimum(selected_connection_item->getMinOffset()); - onConnectionOffsetChanged(selected_connection_item->connection->offset.toInt()); + onConnectionOffsetChanged(selected_connection_item->connection->offset); } void Editor::setSelectedConnectionFromMap(QString mapName) { @@ -1565,7 +1566,7 @@ void Editor::createConnectionItem(MapConnection* connection, bool hide) { } QPixmap pixmap = connected_map->renderConnection(*connection, map->layout); - int offset = connection->offset.toInt(nullptr, 0); + int offset = connection->offset; int x = 0, y = 0; if (connection->direction == "up") { x = offset * 16; @@ -1728,7 +1729,7 @@ void Editor::updateConnectionOffset(int offset) { selected_connection_item->blockSignals(true); offset = qMin(offset, selected_connection_item->getMaxOffset()); offset = qMax(offset, selected_connection_item->getMinOffset()); - selected_connection_item->connection->offset = QString::number(offset); + selected_connection_item->connection->offset = offset; if (selected_connection_item->connection->direction == "up" || selected_connection_item->connection->direction == "down") { selected_connection_item->setX(selected_connection_item->initialX + (offset - selected_connection_item->initialOffset) * 16); } else if (selected_connection_item->connection->direction == "left" || selected_connection_item->connection->direction == "right") { @@ -1747,7 +1748,7 @@ void Editor::setConnectionMap(QString mapName) { if (!selected_connection_item) return; - if (mapName.isEmpty() || mapName == NONE_MAP_NAME) { + if (mapName.isEmpty() || mapName == DYNAMIC_MAP_NAME) { removeCurrentConnection(); return; } @@ -1783,7 +1784,7 @@ void Editor::addNewConnection() { MapConnection* newConnection = new MapConnection; newConnection->direction = minDirection; - newConnection->offset = "0"; + newConnection->offset = 0; newConnection->map_name = defaultMapName; map->connections.append(newConnection); createConnectionItem(newConnection, true); @@ -1851,7 +1852,7 @@ void Editor::updateMirroredConnection(MapConnection* connection, QString origina otherMap->connections.append(mirrorConnection); } - mirrorConnection->offset = QString::number(-connection->offset.toInt()); + mirrorConnection->offset = -connection->offset; } void Editor::removeCurrentConnection() { @@ -1899,7 +1900,7 @@ void Editor::updateDiveEmergeMap(QString mapName, QString direction) { } } - if (mapName.isEmpty() || mapName == NONE_MAP_NAME) { + if (mapName.isEmpty() || mapName == DYNAMIC_MAP_NAME) { // Remove dive/emerge connection if (connection) { map->connections.removeOne(connection); @@ -1909,7 +1910,7 @@ void Editor::updateDiveEmergeMap(QString mapName, QString direction) { if (!connection) { connection = new MapConnection; connection->direction = direction; - connection->offset = "0"; + connection->offset = 0; connection->map_name = mapName; map->connections.append(connection); updateMirroredConnection(connection, connection->direction, connection->map_name); @@ -1952,17 +1953,7 @@ void Editor::toggleBorderVisibility(bool visible, bool enableScriptCallback) void Editor::updateCustomMapHeaderValues(QTableWidget *table) { - 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; - } - map->customHeaders = fields; + map->customHeaders = CustomAttributesTable::getAttributes(table); emit editedMapData(); } diff --git a/src/lib/orderedjson.cpp b/src/lib/orderedjson.cpp index 699c8d12..025607ca 100644 --- a/src/lib/orderedjson.cpp +++ b/src/lib/orderedjson.cpp @@ -308,6 +308,33 @@ const Json & JsonArray::operator[] (int i) const { else return m_value[i]; } +const Json Json::fromQJsonValue(QJsonValue value) { + switch (value.type()) + { + default: + case QJsonValue::String: return value.toString(); + case QJsonValue::Double: return value.toInt(); + case QJsonValue::Bool: return value.toBool(); + case QJsonValue::Array: + { + QJsonArray qArr = value.toArray(); + Json::array arr; + for (const auto &i: qArr) + arr.push_back(Json::fromQJsonValue(i)); + return arr; + } + case QJsonValue::Object: + { + QJsonObject qObj = value.toObject(); + Json::object obj; + for (auto it = qObj.constBegin(); it != qObj.constEnd(); it++) + obj[it.key()] = Json::fromQJsonValue(it.value()); + return obj; + } + } +} + + /* * * * * * * * * * * * * * * * * * * * * Comparison */ diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9c8098b0..3c10824f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -381,28 +381,28 @@ void MainWindow::setProjectSpecificUIVisibility() case BaseGameVersion::pokeruby: ui->checkBox_AllowRunning->setVisible(false); ui->checkBox_AllowBiking->setVisible(false); - ui->checkBox_AllowEscapeRope->setVisible(false); + ui->checkBox_AllowEscaping->setVisible(false); ui->label_AllowRunning->setVisible(false); ui->label_AllowBiking->setVisible(false); - ui->label_AllowEscapeRope->setVisible(false); + ui->label_AllowEscaping->setVisible(false); ui->actionRegion_Map_Editor->setVisible(true); break; case BaseGameVersion::pokeemerald: ui->checkBox_AllowRunning->setVisible(true); ui->checkBox_AllowBiking->setVisible(true); - ui->checkBox_AllowEscapeRope->setVisible(true); + ui->checkBox_AllowEscaping->setVisible(true); ui->label_AllowRunning->setVisible(true); ui->label_AllowBiking->setVisible(true); - ui->label_AllowEscapeRope->setVisible(true); + ui->label_AllowEscaping->setVisible(true); ui->actionRegion_Map_Editor->setVisible(true); break; case BaseGameVersion::pokefirered: ui->checkBox_AllowRunning->setVisible(true); ui->checkBox_AllowBiking->setVisible(true); - ui->checkBox_AllowEscapeRope->setVisible(true); + ui->checkBox_AllowEscaping->setVisible(true); ui->label_AllowRunning->setVisible(true); ui->label_AllowBiking->setVisible(true); - ui->label_AllowEscapeRope->setVisible(true); + ui->label_AllowEscaping->setVisible(true); ui->actionRegion_Map_Editor->setVisible(true); break; } @@ -719,6 +719,10 @@ void MainWindow::refreshMapScene() } void MainWindow::openWarpMap(QString map_name, int event_id, Event::Group event_group) { + // Can't warp to dynamic maps + if (map_name == DYNAMIC_MAP_NAME) + return; + // Ensure valid destination map name. if (!editor->project->mapNames.contains(map_name)) { logError(QString("Invalid map name '%1'").arg(map_name)); @@ -737,10 +741,10 @@ void MainWindow::openWarpMap(QString map_name, int event_id, Event::Group event_ } // Select the target event. - event_id -= Event::getIndexOffset(event_group); + int index = event_id - Event::getIndexOffset(event_group); QList events = editor->map->events[event_group]; - if (event_id < events.length() && event_id >= 0) { - Event *event = events.at(event_id); + if (index < events.length() && index >= 0) { + Event *event = events.at(index); for (DraggablePixmapItem *item : editor->getObjects()) { if (item->event == event) { editor->selected_events->clear(); @@ -748,6 +752,9 @@ void MainWindow::openWarpMap(QString map_name, int event_id, Event::Group event_ editor->updateSelectedEvents(); } } + } else { + // Can still warp to this map, but can't select the specified event + logWarn(QString("%1 %2 doesn't exist on map '%3'").arg(Event::eventGroupToString(event_group)).arg(event_id).arg(map_name)); } } @@ -769,13 +776,13 @@ void MainWindow::displayMapProperties() { const QSignalBlocker blockerA(ui->checkBox_AllowRunning); const QSignalBlocker blockerB(ui->checkBox_AllowBiking); const QSignalBlocker blockerC(ui->spinBox_FloorNumber); - const QSignalBlocker blockerD(ui->checkBox_AllowEscapeRope); + const QSignalBlocker blockerD(ui->checkBox_AllowEscaping); ui->checkBox_Visibility->setChecked(false); ui->checkBox_ShowLocation->setChecked(false); ui->checkBox_AllowRunning->setChecked(false); ui->checkBox_AllowBiking->setChecked(false); - ui->checkBox_AllowEscapeRope->setChecked(false); + ui->checkBox_AllowEscaping->setChecked(false); if (!editor || !editor->map || !editor->project) { ui->frame_3->setEnabled(false); return; @@ -793,27 +800,24 @@ void MainWindow::displayMapProperties() { ui->comboBox_Song->setCurrentText(map->song); ui->comboBox_Location->setCurrentText(map->location); - ui->checkBox_Visibility->setChecked(ParseUtil::gameStringToBool(map->requiresFlash)); + ui->checkBox_Visibility->setChecked(map->requiresFlash); ui->comboBox_Weather->setCurrentText(map->weather); ui->comboBox_Type->setCurrentText(map->type); ui->comboBox_BattleScene->setCurrentText(map->battle_scene); - ui->checkBox_ShowLocation->setChecked(ParseUtil::gameStringToBool(map->show_location)); + ui->checkBox_ShowLocation->setChecked(map->show_location); if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - ui->checkBox_AllowRunning->setChecked(ParseUtil::gameStringToBool(map->allowRunning)); - ui->checkBox_AllowBiking->setChecked(ParseUtil::gameStringToBool(map->allowBiking)); - ui->checkBox_AllowEscapeRope->setChecked(ParseUtil::gameStringToBool(map->allowEscapeRope)); + ui->checkBox_AllowRunning->setChecked(map->allowRunning); + ui->checkBox_AllowBiking->setChecked(map->allowBiking); + ui->checkBox_AllowEscaping->setChecked(map->allowEscaping); } ui->spinBox_FloorNumber->setValue(map->floorNumber); // Custom fields table. ui->tableWidget_CustomHeaderFields->blockSignals(true); ui->tableWidget_CustomHeaderFields->setRowCount(0); - for (auto it = map->customHeaders.begin(); it != map->customHeaders.end(); it++) { - int rowIndex = ui->tableWidget_CustomHeaderFields->rowCount(); - ui->tableWidget_CustomHeaderFields->insertRow(rowIndex); - ui->tableWidget_CustomHeaderFields->setItem(rowIndex, 0, new QTableWidgetItem(it.key())); - ui->tableWidget_CustomHeaderFields->setItem(rowIndex, 1, new QTableWidgetItem(it.value())); - } + for (auto it = map->customHeaders.begin(); it != map->customHeaders.end(); it++) + CustomAttributesTable::addAttribute(ui->tableWidget_CustomHeaderFields, it.key(), it.value()); + ui->tableWidget_CustomHeaderFields->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->tableWidget_CustomHeaderFields->blockSignals(false); } @@ -860,12 +864,7 @@ void MainWindow::on_comboBox_BattleScene_currentTextChanged(const QString &battl void MainWindow::on_checkBox_Visibility_stateChanged(int selected) { if (editor && editor->map) { - bool checked = selected == Qt::Checked; - if (checked) { - editor->map->requiresFlash = "TRUE"; - } else { - editor->map->requiresFlash = "FALSE"; - } + editor->map->requiresFlash = (selected == Qt::Checked); markMapEdited(); } } @@ -873,12 +872,7 @@ void MainWindow::on_checkBox_Visibility_stateChanged(int selected) void MainWindow::on_checkBox_ShowLocation_stateChanged(int selected) { if (editor && editor->map) { - bool checked = selected == Qt::Checked; - if (checked) { - editor->map->show_location = "TRUE"; - } else { - editor->map->show_location = "FALSE"; - } + editor->map->show_location = (selected == Qt::Checked); markMapEdited(); } } @@ -886,12 +880,7 @@ void MainWindow::on_checkBox_ShowLocation_stateChanged(int selected) void MainWindow::on_checkBox_AllowRunning_stateChanged(int selected) { if (editor && editor->map) { - bool checked = selected == Qt::Checked; - if (checked) { - editor->map->allowRunning = "1"; - } else { - editor->map->allowRunning = "0"; - } + editor->map->allowRunning = (selected == Qt::Checked); markMapEdited(); } } @@ -899,25 +888,15 @@ void MainWindow::on_checkBox_AllowRunning_stateChanged(int selected) void MainWindow::on_checkBox_AllowBiking_stateChanged(int selected) { if (editor && editor->map) { - bool checked = selected == Qt::Checked; - if (checked) { - editor->map->allowBiking = "1"; - } else { - editor->map->allowBiking = "0"; - } + editor->map->allowBiking = (selected == Qt::Checked); markMapEdited(); } } -void MainWindow::on_checkBox_AllowEscapeRope_stateChanged(int selected) +void MainWindow::on_checkBox_AllowEscaping_stateChanged(int selected) { if (editor && editor->map) { - bool checked = selected == Qt::Checked; - if (checked) { - editor->map->allowEscapeRope = "1"; - } else { - editor->map->allowEscapeRope = "0"; - } + editor->map->allowEscaping = (selected == Qt::Checked); markMapEdited(); } } @@ -1202,7 +1181,7 @@ void MainWindow::onNewMapCreated() { sortMapList(); setMap(newMapName, true); - if (ParseUtil::gameStringToBool(newMap->isFlyable)) { + if (newMap->needsHealLocation) { addNewEvent(Event::Type::HealLocation); editor->project->saveHealLocations(newMap); editor->save(); @@ -1605,8 +1584,8 @@ void MainWindow::paste() { } QJsonArray metatilesArray = pasteObject["metatile_selection"].toArray(); QJsonArray collisionsArray = pasteObject["collision_selection"].toArray(); - int width = pasteObject["width"].toInt(); - int height = pasteObject["height"].toInt(); + int width = ParseUtil::jsonToInt(pasteObject["width"]); + int height = ParseUtil::jsonToInt(pasteObject["height"]); QList metatiles; QList> collisions; for (auto tile : metatilesArray) { @@ -1912,25 +1891,20 @@ void MainWindow::addNewEvent(Event::Type type) { } } +void MainWindow::tryAddEventTab(QWidget * tab, Event::Group group) { + if (editor->map->events.value(group).length()) + ui->tabWidget_EventType->addTab(tab, QString("%1s").arg(Event::eventGroupToString(group))); +} + void MainWindow::displayEventTabs() { const QSignalBlocker blocker(ui->tabWidget_EventType); ui->tabWidget_EventType->clear(); - - if (editor->map->events.value(Event::Group::Object).length()) - ui->tabWidget_EventType->addTab(eventTabObjectWidget, "Objects"); - - if (editor->map->events.value(Event::Group::Warp).length()) - ui->tabWidget_EventType->addTab(eventTabWarpWidget, "Warps"); - - if (editor->map->events.value(Event::Group::Coord).length()) - ui->tabWidget_EventType->addTab(eventTabTriggerWidget, "Triggers"); - - if (editor->map->events.value(Event::Group::Bg).length()) - ui->tabWidget_EventType->addTab(eventTabBGWidget, "BGs"); - - if (editor->map->events.value(Event::Group::Heal).length()) - ui->tabWidget_EventType->addTab(eventTabHealspotWidget, "Healspots"); + tryAddEventTab(eventTabObjectWidget, Event::Group::Object); + tryAddEventTab(eventTabWarpWidget, Event::Group::Warp); + tryAddEventTab(eventTabTriggerWidget, Event::Group::Coord); + tryAddEventTab(eventTabBGWidget, Event::Group::Bg); + tryAddEventTab(eventTabHealspotWidget, Event::Group::Heal); } void MainWindow::updateObjects() { @@ -2785,33 +2759,18 @@ void MainWindow::togglePreferenceSpecificUi() { void MainWindow::on_pushButton_AddCustomHeaderField_clicked() { - int rowIndex = this->ui->tableWidget_CustomHeaderFields->rowCount(); - this->ui->tableWidget_CustomHeaderFields->insertRow(rowIndex); - this->ui->tableWidget_CustomHeaderFields->selectRow(rowIndex); - this->editor->updateCustomMapHeaderValues(this->ui->tableWidget_CustomHeaderFields); + bool ok; + QJsonValue value = CustomAttributesTable::pickType(this, &ok); + if (ok){ + CustomAttributesTable::addAttribute(this->ui->tableWidget_CustomHeaderFields, "", value, true); + this->editor->updateCustomMapHeaderValues(this->ui->tableWidget_CustomHeaderFields); + } } void MainWindow::on_pushButton_DeleteCustomHeaderField_clicked() { - int rowCount = this->ui->tableWidget_CustomHeaderFields->rowCount(); - if (rowCount > 0) { - QModelIndexList indexList = ui->tableWidget_CustomHeaderFields->selectionModel()->selectedIndexes(); - QList persistentIndexes; - for (QModelIndex index : indexList) { - QPersistentModelIndex persistentIndex(index); - persistentIndexes.append(persistentIndex); - } - - for (QPersistentModelIndex index : persistentIndexes) { - this->ui->tableWidget_CustomHeaderFields->removeRow(index.row()); - } - - if (this->ui->tableWidget_CustomHeaderFields->rowCount() > 0) { - this->ui->tableWidget_CustomHeaderFields->selectRow(0); - } - + if (CustomAttributesTable::deleteSelectedAttributes(this->ui->tableWidget_CustomHeaderFields)) this->editor->updateCustomMapHeaderValues(this->ui->tableWidget_CustomHeaderFields); - } } void MainWindow::on_tableWidget_CustomHeaderFields_cellChanged(int, int) diff --git a/src/project.cpp b/src/project.cpp index fb542c28..f5f58261 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -142,36 +142,36 @@ void Project::setNewMapConnections(Map *map) { map->connections.clear(); } -static QMap defaultTopLevelMapFields { - {"id", true}, - {"name", true}, - {"layout", true}, - {"music", true}, - {"region_map_section", true}, - {"requires_flash", true}, - {"weather", true}, - {"map_type", true}, - {"show_map_name", true}, - {"battle_scene", true}, - {"connections", true}, - {"object_events", true}, - {"warp_events", true}, - {"coord_events", true}, - {"bg_events", true}, - {"shared_events_map", true}, - {"shared_scripts_map", true}, +const QSet defaultTopLevelMapFields = { + "id", + "name", + "layout", + "music", + "region_map_section", + "requires_flash", + "weather", + "map_type", + "show_map_name", + "battle_scene", + "connections", + "object_events", + "warp_events", + "coord_events", + "bg_events", + "shared_events_map", + "shared_scripts_map", }; -QMap Project::getTopLevelMapFields() { - QMap topLevelMapFields = defaultTopLevelMapFields; +QSet Project::getTopLevelMapFields() { + QSet topLevelMapFields = defaultTopLevelMapFields; if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - topLevelMapFields.insert("allow_cycling", true); - topLevelMapFields.insert("allow_escaping", true); - topLevelMapFields.insert("allow_running", true); + topLevelMapFields.insert("allow_cycling"); + topLevelMapFields.insert("allow_escaping"); + topLevelMapFields.insert("allow_running"); } if (projectConfig.getFloorNumberEnabled()) { - topLevelMapFields.insert("floor_number", true); + topLevelMapFields.insert("floor_number"); } return topLevelMapFields; } @@ -190,25 +190,25 @@ bool Project::loadMapData(Map* map) { QJsonObject mapObj = mapDoc.object(); - map->song = mapObj["music"].toString(); - map->layoutId = mapObj["layout"].toString(); - map->location = mapObj["region_map_section"].toString(); - map->requiresFlash = QString::number(mapObj["requires_flash"].toBool()); - map->weather = mapObj["weather"].toString(); - map->type = mapObj["map_type"].toString(); - map->requiresFlash = QString::number(mapObj["requires_flash"].toBool()); - map->show_location = QString::number(mapObj["show_map_name"].toBool()); - map->battle_scene = mapObj["battle_scene"].toString(); + map->song = ParseUtil::jsonToQString(mapObj["music"]); + map->layoutId = ParseUtil::jsonToQString(mapObj["layout"]); + map->location = ParseUtil::jsonToQString(mapObj["region_map_section"]); + map->requiresFlash = ParseUtil::jsonToBool(mapObj["requires_flash"]); + map->weather = ParseUtil::jsonToQString(mapObj["weather"]); + map->type = ParseUtil::jsonToQString(mapObj["map_type"]); + map->show_location = ParseUtil::jsonToBool(mapObj["show_map_name"]); + map->battle_scene = ParseUtil::jsonToQString(mapObj["battle_scene"]); + if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - map->allowBiking = QString::number(mapObj["allow_cycling"].toBool()); - map->allowEscapeRope = QString::number(mapObj["allow_escaping"].toBool()); - map->allowRunning = QString::number(mapObj["allow_running"].toBool()); + map->allowBiking = ParseUtil::jsonToBool(mapObj["allow_cycling"]); + map->allowEscaping = ParseUtil::jsonToBool(mapObj["allow_escaping"]); + map->allowRunning = ParseUtil::jsonToBool(mapObj["allow_running"]); } if (projectConfig.getFloorNumberEnabled()) { - map->floorNumber = mapObj["floor_number"].toInt(); + map->floorNumber = ParseUtil::jsonToInt(mapObj["floor_number"]); } - map->sharedEventsMap = mapObj["shared_events_map"].toString(); - map->sharedScriptsMap = mapObj["shared_scripts_map"].toString(); + map->sharedEventsMap = ParseUtil::jsonToQString(mapObj["shared_events_map"]); + map->sharedScriptsMap = ParseUtil::jsonToQString(mapObj["shared_scripts_map"]); // Events map->events[Event::Group::Object].clear(); @@ -217,7 +217,7 @@ bool Project::loadMapData(Map* map) { for (int i = 0; i < objectEventsArr.size(); i++) { QJsonObject event = objectEventsArr[i].toObject(); // If clone objects are not enabled then no type field is present - QString type = hasCloneObjects ? event["type"].toString() : "object"; + QString type = hasCloneObjects ? ParseUtil::jsonToQString(event["type"]) : "object"; if (type.isEmpty() || type == "object") { ObjectEvent *object = new ObjectEvent(); object->loadFromJson(event, this); @@ -252,7 +252,7 @@ bool Project::loadMapData(Map* map) { QJsonArray coordEventsArr = mapObj["coord_events"].toArray(); for (int i = 0; i < coordEventsArr.size(); i++) { QJsonObject event = coordEventsArr[i].toObject(); - QString type = event["type"].toString(); + QString type = ParseUtil::jsonToQString(event["type"]); if (type == "trigger") { TriggerEvent *coord = new TriggerEvent(); coord->loadFromJson(event, this); @@ -270,7 +270,7 @@ bool Project::loadMapData(Map* map) { QJsonArray bgEventsArr = mapObj["bg_events"].toArray(); for (int i = 0; i < bgEventsArr.size(); i++) { QJsonObject event = bgEventsArr[i].toObject(); - QString type = event["type"].toString(); + QString type = ParseUtil::jsonToQString(event["type"]); if (type == "sign") { SignEvent *bg = new SignEvent(); bg->loadFromJson(event, this); @@ -319,9 +319,9 @@ bool Project::loadMapData(Map* map) { for (int i = 0; i < connectionsArr.size(); i++) { QJsonObject connectionObj = connectionsArr[i].toObject(); MapConnection *connection = new MapConnection; - connection->direction = connectionObj["direction"].toString(); - connection->offset = QString::number(connectionObj["offset"].toInt()); - QString mapConstant = connectionObj["map"].toString(); + connection->direction = ParseUtil::jsonToQString(connectionObj["direction"]); + connection->offset = ParseUtil::jsonToInt(connectionObj["offset"]); + QString mapConstant = ParseUtil::jsonToQString(connectionObj["map"]); if (mapConstantsToMapNames.contains(mapConstant)) { connection->map_name = mapConstantsToMapNames.value(mapConstant); map->connections.append(connection); @@ -332,10 +332,10 @@ bool Project::loadMapData(Map* map) { } // Check for custom fields - QMap baseFields = this->getTopLevelMapFields(); + QSet baseFields = this->getTopLevelMapFields(); for (QString key : mapObj.keys()) { if (!baseFields.contains(key)) { - map->customHeaders.insert(key, mapObj[key].toString()); + map->customHeaders.insert(key, mapObj[key]); } } @@ -355,7 +355,7 @@ QString Project::readMapLayoutId(QString map_name) { } QJsonObject mapObj = mapDoc.object(); - return mapObj["layout"].toString(); + return ParseUtil::jsonToQString(mapObj["layout"]); } QString Project::readMapLocation(QString map_name) { @@ -371,23 +371,21 @@ QString Project::readMapLocation(QString map_name) { } QJsonObject mapObj = mapDoc.object(); - return mapObj["region_map_section"].toString(); + return ParseUtil::jsonToQString(mapObj["region_map_section"]); } void Project::setNewMapHeader(Map* map, int mapIndex) { map->layoutId = QString("%1").arg(mapIndex); map->location = mapSectionValueToName.value(0); - map->requiresFlash = "FALSE"; + map->requiresFlash = false; map->weather = weatherNames.value(0, "WEATHER_NONE"); map->type = mapTypes.value(0, "MAP_TYPE_NONE"); map->song = defaultSong; - if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokeruby) { - map->show_location = "TRUE"; - } else { - map->allowBiking = "1"; - map->allowEscapeRope = "0"; - map->allowRunning = "1"; - map->show_location = "1"; + map->show_location = true; + if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { + map->allowBiking = true; + map->allowEscaping = false; + map->allowRunning = true; } if (projectConfig.getFloorNumberEnabled()) { map->floorNumber = 0; @@ -445,7 +443,7 @@ bool Project::readMapLayouts() { return false; } - layoutsLabel = layoutsObj["layouts_table_label"].toString(); + layoutsLabel = ParseUtil::jsonToQString(layoutsObj["layouts_table_label"]); if (layoutsLabel.isNull()) { layoutsLabel = "gMapLayouts"; logWarn(QString("'layouts_table_label' value is missing from %1. Defaulting to %2") @@ -477,61 +475,61 @@ bool Project::readMapLayouts() { return false; } MapLayout *layout = new MapLayout(); - layout->id = layoutObj["id"].toString(); + layout->id = ParseUtil::jsonToQString(layoutObj["id"]); if (layout->id.isEmpty()) { logError(QString("Missing 'id' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); return false; } - layout->name = layoutObj["name"].toString(); + layout->name = ParseUtil::jsonToQString(layoutObj["name"]); if (layout->name.isEmpty()) { logError(QString("Missing 'name' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); return false; } - int lwidth = layoutObj["width"].toInt(); + int lwidth = ParseUtil::jsonToInt(layoutObj["width"]); if (lwidth <= 0) { logError(QString("Invalid layout 'width' value '%1' on layout %2 in %3. Must be greater than 0.").arg(lwidth).arg(i).arg(layoutsFilepath)); return false; } - layout->width = QString::number(lwidth); - int lheight = layoutObj["height"].toInt(); + layout->width = lwidth; + int lheight = ParseUtil::jsonToInt(layoutObj["height"]); if (lheight <= 0) { logError(QString("Invalid layout 'height' value '%1' on layout %2 in %3. Must be greater than 0.").arg(lheight).arg(i).arg(layoutsFilepath)); return false; } - layout->height = QString::number(lheight); + layout->height = lheight; if (useCustomBorderSize) { - int bwidth = layoutObj["border_width"].toInt(); + int bwidth = ParseUtil::jsonToInt(layoutObj["border_width"]); if (bwidth <= 0) { // 0 is an expected border width/height that should be handled, GF used it for the RS layouts in FRLG logWarn(QString("Invalid layout 'border_width' value '%1' on layout %2 in %3. Must be greater than 0. Using default (%4) instead.").arg(bwidth).arg(i).arg(layoutsFilepath).arg(DEFAULT_BORDER_WIDTH)); bwidth = DEFAULT_BORDER_WIDTH; } - layout->border_width = QString::number(bwidth); - int bheight = layoutObj["border_height"].toInt(); + layout->border_width = bwidth; + int bheight = ParseUtil::jsonToInt(layoutObj["border_height"]); if (bheight <= 0) { logWarn(QString("Invalid layout 'border_height' value '%1' on layout %2 in %3. Must be greater than 0. Using default (%4) instead.").arg(bheight).arg(i).arg(layoutsFilepath).arg(DEFAULT_BORDER_HEIGHT)); bheight = DEFAULT_BORDER_HEIGHT; } - layout->border_height = QString::number(bheight); + layout->border_height = bheight; } else { - layout->border_width = QString::number(DEFAULT_BORDER_WIDTH); - layout->border_height = QString::number(DEFAULT_BORDER_HEIGHT); + layout->border_width = DEFAULT_BORDER_WIDTH; + layout->border_height = DEFAULT_BORDER_HEIGHT; } - layout->tileset_primary_label = layoutObj["primary_tileset"].toString(); + layout->tileset_primary_label = ParseUtil::jsonToQString(layoutObj["primary_tileset"]); if (layout->tileset_primary_label.isEmpty()) { logError(QString("Missing 'primary_tileset' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); return false; } - layout->tileset_secondary_label = layoutObj["secondary_tileset"].toString(); + layout->tileset_secondary_label = ParseUtil::jsonToQString(layoutObj["secondary_tileset"]); if (layout->tileset_secondary_label.isEmpty()) { logError(QString("Missing 'secondary_tileset' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); return false; } - layout->border_path = layoutObj["border_filepath"].toString(); + layout->border_path = ParseUtil::jsonToQString(layoutObj["border_filepath"]); if (layout->border_path.isEmpty()) { logError(QString("Missing 'border_filepath' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); return false; } - layout->blockdata_path = layoutObj["blockdata_filepath"].toString(); + layout->blockdata_path = ParseUtil::jsonToQString(layoutObj["blockdata_filepath"]); if (layout->blockdata_path.isEmpty()) { logError(QString("Missing 'blockdata_filepath' value on layout %1 in %2").arg(i).arg(layoutsFilepath)); return false; @@ -566,11 +564,11 @@ void Project::saveMapLayouts() { OrderedJson::object layoutObj; layoutObj["id"] = layout->id; layoutObj["name"] = layout->name; - layoutObj["width"] = layout->width.toInt(nullptr, 0); - layoutObj["height"] = layout->height.toInt(nullptr, 0); + layoutObj["width"] = layout->width; + layoutObj["height"] = layout->height; if (useCustomBorderSize) { - layoutObj["border_width"] = layout->border_width.toInt(nullptr, 0); - layoutObj["border_height"] = layout->border_height.toInt(nullptr, 0); + layoutObj["border_width"] = layout->border_width; + layoutObj["border_height"] = layout->border_height; } layoutObj["primary_tileset"] = layout->tileset_primary_label; layoutObj["secondary_tileset"] = layout->tileset_secondary_label; @@ -597,10 +595,10 @@ void Project::setNewMapLayout(Map* map) { MapLayout *layout = new MapLayout(); layout->id = MapLayout::layoutConstantFromName(map->name); layout->name = QString("%1_Layout").arg(map->name); - layout->width = QString::number(getDefaultMapSize()); - layout->height = QString::number(getDefaultMapSize()); - layout->border_width = QString::number(DEFAULT_BORDER_WIDTH); - layout->border_height = QString::number(DEFAULT_BORDER_HEIGHT); + layout->width = getDefaultMapSize(); + layout->height = getDefaultMapSize(); + layout->border_width = DEFAULT_BORDER_WIDTH; + layout->border_height = DEFAULT_BORDER_HEIGHT; layout->border_path = QString("%2%1/border.bin").arg(map->name).arg(projectConfig.getFilePath(ProjectFilePath::data_layouts_folders)); layout->blockdata_path = QString("%2%1/map.bin").arg(map->name).arg(projectConfig.getFilePath(ProjectFilePath::data_layouts_folders)); layout->tileset_primary_label = tilesetLabels["primary"].value(0, "gTileset_General"); @@ -1254,11 +1252,11 @@ void Project::saveMap(Map *map) { QJsonObject newLayoutObj; newLayoutObj["id"] = map->layout->id; newLayoutObj["name"] = map->layout->name; - newLayoutObj["width"] = map->layout->width.toInt(); - newLayoutObj["height"] = map->layout->height.toInt(); + newLayoutObj["width"] = map->layout->width; + newLayoutObj["height"] = map->layout->height; if (projectConfig.getUseCustomBorderSize()) { - newLayoutObj["border_width"] = map->layout->border_width.toInt(); - newLayoutObj["border_height"] = map->layout->border_height.toInt(); + newLayoutObj["border_width"] = map->layout->border_width; + newLayoutObj["border_height"] = map->layout->border_height; } newLayoutObj["primary_tileset"] = map->layout->tileset_primary_label; newLayoutObj["secondary_tileset"] = map->layout->tileset_secondary_label; @@ -1283,15 +1281,15 @@ void Project::saveMap(Map *map) { mapObj["layout"] = map->layout->id; mapObj["music"] = map->song; mapObj["region_map_section"] = map->location; - mapObj["requires_flash"] = ParseUtil::gameStringToBool(map->requiresFlash); + mapObj["requires_flash"] = map->requiresFlash; mapObj["weather"] = map->weather; mapObj["map_type"] = map->type; if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - mapObj["allow_cycling"] = ParseUtil::gameStringToBool(map->allowBiking); - mapObj["allow_escaping"] = ParseUtil::gameStringToBool(map->allowEscapeRope); - mapObj["allow_running"] = ParseUtil::gameStringToBool(map->allowRunning); + mapObj["allow_cycling"] = map->allowBiking; + mapObj["allow_escaping"] = map->allowEscaping; + mapObj["allow_running"] = map->allowRunning; } - mapObj["show_map_name"] = ParseUtil::gameStringToBool(map->show_location); + mapObj["show_map_name"] = map->show_location; if (projectConfig.getFloorNumberEnabled()) { mapObj["floor_number"] = map->floorNumber; } @@ -1304,7 +1302,7 @@ void Project::saveMap(Map *map) { if (mapNamesToMapConstants.contains(connection->map_name)) { OrderedJson::object connectionObj; connectionObj["map"] = this->mapNamesToMapConstants.value(connection->map_name); - connectionObj["offset"] = connection->offset.toInt(); + connectionObj["offset"] = connection->offset; connectionObj["direction"] = connection->direction; connectionsArr.append(connectionObj); } else { @@ -1363,7 +1361,7 @@ void Project::saveMap(Map *map) { // Custom header fields. for (QString key : map->customHeaders.keys()) { - mapObj[key] = map->customHeaders[key]; + mapObj[key] = OrderedJson::fromQJsonValue(map->customHeaders[key]); } OrderedJson mapJson(mapObj); @@ -1765,12 +1763,12 @@ bool Project::readMapGroups() { QStringList maps; QStringList groups; for (int groupIndex = 0; groupIndex < mapGroupOrder.size(); groupIndex++) { - QString groupName = mapGroupOrder.at(groupIndex).toString(); + QString groupName = ParseUtil::jsonToQString(mapGroupOrder.at(groupIndex)); QJsonArray mapNames = mapGroupsObj.value(groupName).toArray(); groupedMaps.append(QStringList()); groups.append(groupName); for (int j = 0; j < mapNames.size(); j++) { - QString mapName = mapNames.at(j).toString(); + QString mapName = ParseUtil::jsonToQString(mapNames.at(j)); mapGroups.insert(mapName, groupIndex); groupedMaps[groupIndex].append(mapName); maps.append(mapName); @@ -1782,9 +1780,9 @@ bool Project::readMapGroups() { } } - mapConstantsToMapNames.insert(NONE_MAP_CONSTANT, NONE_MAP_NAME); - mapNamesToMapConstants.insert(NONE_MAP_NAME, NONE_MAP_CONSTANT); - maps.append(NONE_MAP_NAME); + mapConstantsToMapNames.insert(DYNAMIC_MAP_CONSTANT, DYNAMIC_MAP_NAME); + mapNamesToMapConstants.insert(DYNAMIC_MAP_NAME, DYNAMIC_MAP_CONSTANT); + maps.append(DYNAMIC_MAP_NAME); groupNames = groups; groupedMapNames = groupedMaps; diff --git a/src/scriptapi/apimap.cpp b/src/scriptapi/apimap.cpp index 9451d1ee..b133a74b 100644 --- a/src/scriptapi/apimap.cpp +++ b/src/scriptapi/apimap.cpp @@ -831,7 +831,7 @@ void MainWindow::setLocation(QString location) { bool MainWindow::getRequiresFlash() { if (!this->editor || !this->editor->map) return false; - return ParseUtil::gameStringToBool(this->editor->map->requiresFlash); + return this->editor->map->requiresFlash; } void MainWindow::setRequiresFlash(bool require) { @@ -891,7 +891,7 @@ void MainWindow::setBattleScene(QString battleScene) { bool MainWindow::getShowLocationName() { if (!this->editor || !this->editor->map) return false; - return ParseUtil::gameStringToBool(this->editor->map->show_location); + return this->editor->map->show_location; } void MainWindow::setShowLocationName(bool show) { @@ -903,7 +903,7 @@ void MainWindow::setShowLocationName(bool show) { bool MainWindow::getAllowRunning() { if (!this->editor || !this->editor->map) return false; - return ParseUtil::gameStringToBool(this->editor->map->allowRunning); + return this->editor->map->allowRunning; } void MainWindow::setAllowRunning(bool allow) { @@ -915,7 +915,7 @@ void MainWindow::setAllowRunning(bool allow) { bool MainWindow::getAllowBiking() { if (!this->editor || !this->editor->map) return false; - return ParseUtil::gameStringToBool(this->editor->map->allowBiking); + return this->editor->map->allowBiking; } void MainWindow::setAllowBiking(bool allow) { @@ -927,13 +927,13 @@ void MainWindow::setAllowBiking(bool allow) { bool MainWindow::getAllowEscaping() { if (!this->editor || !this->editor->map) return false; - return ParseUtil::gameStringToBool(this->editor->map->allowEscapeRope); + return this->editor->map->allowEscaping; } void MainWindow::setAllowEscaping(bool allow) { if (!this->ui) return; - this->ui->checkBox_AllowEscapeRope->setChecked(allow); + this->ui->checkBox_AllowEscaping->setChecked(allow); } int MainWindow::getFloorNumber() { diff --git a/src/ui/connectionpixmapitem.cpp b/src/ui/connectionpixmapitem.cpp index 221c1911..3fd09dda 100644 --- a/src/ui/connectionpixmapitem.cpp +++ b/src/ui/connectionpixmapitem.cpp @@ -56,7 +56,7 @@ QVariant ConnectionPixmapItem::itemChange(GraphicsItemChange change, const QVari y = this->initialY; } - this->connection->offset = QString::number(newOffset); + this->connection->offset = newOffset; emit connectionMoved(this->connection); return QPointF(x, y); } diff --git a/src/ui/customattributestable.cpp b/src/ui/customattributestable.cpp index 4c41a104..83667483 100644 --- a/src/ui/customattributestable.cpp +++ b/src/ui/customattributestable.cpp @@ -1,10 +1,12 @@ #include "customattributestable.h" +#include "parseutil.h" #include #include #include #include #include #include +#include CustomAttributesTable::CustomAttributesTable(Event *event, QWidget *parent) : QFrame(parent) @@ -28,52 +30,34 @@ CustomAttributesTable::CustomAttributesTable(Event *event, QWidget *parent) : layout->addWidget(buttonsFrame); this->table = new QTableWidget(this); - this->table->setColumnCount(2); - this->table->setHorizontalHeaderLabels(QStringList({"Key", "Value"})); + this->table->setColumnCount(3); + this->table->setHorizontalHeaderLabels(QStringList({"Type", "Key", "Value"})); this->table->horizontalHeader()->setStretchLastSection(true); layout->addWidget(this->table); - QMap customValues = this->event->getCustomValues(); - for (auto it = customValues.begin(); it != 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())); - } + QMap customValues = this->event->getCustomValues(); + for (auto it = customValues.begin(); it != customValues.end(); it++) + CustomAttributesTable::addAttribute(this->table, it.key(), it.value()); connect(addButton, &QPushButton::clicked, [=]() { - int rowIndex = this->table->rowCount(); - this->table->insertRow(rowIndex); - this->table->selectRow(rowIndex); - this->event->setCustomValues(this->getTableFields()); - this->resizeVertically(); + bool ok; + QJsonValue value = CustomAttributesTable::pickType(this, &ok); + if (ok){ + CustomAttributesTable::addAttribute(this->table, "", value, true); + this->event->setCustomValues(CustomAttributesTable::getAttributes(this->table)); + 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->setCustomValues(this->getTableFields()); + if (CustomAttributesTable::deleteSelectedAttributes(this->table)) { + this->event->setCustomValues(CustomAttributesTable::getAttributes(this->table)); this->resizeVertically(); } }); connect(this->table, &QTableWidget::cellChanged, [=]() { - this->event->setCustomValues(this->getTableFields()); + this->event->setCustomValues(CustomAttributesTable::getAttributes(this->table)); }); this->resizeVertically(); @@ -83,20 +67,6 @@ 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; @@ -112,3 +82,125 @@ void CustomAttributesTable::resizeVertically() { this->table->setMinimumHeight(totalHeight); this->table->setMaximumHeight(totalHeight); } + +const QMap CustomAttributesTable::getAttributes(QTableWidget * table) { + QMap fields; + if (!table) return fields; + + for (int row = 0; row < table->rowCount(); row++) { + QString key = ""; + QTableWidgetItem *typeItem = table->item(row, 0); + QTableWidgetItem *keyItem = table->item(row, 1); + QTableWidgetItem *valueItem = table->item(row, 2); + + if (keyItem) key = keyItem->text(); + if (key.isEmpty() || !typeItem || !valueItem) + continue; + + // Read from the table data which JSON type to save the value as + QJsonValue::Type type = static_cast(typeItem->data(Qt::UserRole).toInt()); + QJsonValue value; + switch (type) + { + case QJsonValue::String: + value = QJsonValue(valueItem->text()); + break; + case QJsonValue::Double: + value = QJsonValue(valueItem->text().toInt()); + break; + case QJsonValue::Bool: + value = QJsonValue(valueItem->checkState() == Qt::Checked); + break; + default: + // All other types will just be preserved + value = valueItem->data(Qt::UserRole).toJsonValue(); + break; + } + fields[key] = value; + } + return fields; +} + +QJsonValue CustomAttributesTable::pickType(QWidget * parent, bool * ok) { + const QMap valueTypes = { + {"String", QJsonValue(QString(""))}, + {"Number", QJsonValue(0)}, + {"Boolean", QJsonValue(false)}, + }; + QStringList typeNames = valueTypes.keys(); + QString selection = QInputDialog::getItem(parent, "", "Choose Value Type", typeNames, typeNames.indexOf("String"), false, ok); + return valueTypes.value(selection); +} + +void CustomAttributesTable::addAttribute(QTableWidget * table, QString key, QJsonValue value, bool isNew) { + if (!table) return; + QTableWidgetItem * valueItem; + QJsonValue::Type type = value.type(); + switch (type) + { + case QJsonValue::String: + case QJsonValue::Double: + valueItem = new QTableWidgetItem(ParseUtil::jsonToQString(value)); + break; + case QJsonValue::Bool: + valueItem = new QTableWidgetItem(""); + valueItem->setCheckState(value.toBool() ? Qt::Checked : Qt::Unchecked); + valueItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + break; + default: + valueItem = new QTableWidgetItem("This value cannot be edited from this table"); + valueItem->setFlags(Qt::ItemIsSelectable); + valueItem->setData(Qt::UserRole, value); // Preserve the value for writing to the file + break; + } + + const QHash typeToName = { + {QJsonValue::Bool, "Bool"}, + {QJsonValue::Double, "Number"}, + {QJsonValue::String, "String"}, + {QJsonValue::Array, "Array"}, + {QJsonValue::Object, "Object"}, + {QJsonValue::Null, "Null"}, + {QJsonValue::Undefined, "Null"}, + }; + QTableWidgetItem * typeItem = new QTableWidgetItem(typeToName[type]); + typeItem->setFlags(Qt::ItemIsEnabled); + typeItem->setData(Qt::UserRole, type); // Record the type for writing to the file + typeItem->setTextAlignment(Qt::AlignCenter); + + int rowIndex = table->rowCount(); + table->insertRow(rowIndex); + table->setItem(rowIndex, 0, typeItem); + table->setItem(rowIndex, 1, new QTableWidgetItem(key)); + table->setItem(rowIndex, 2, valueItem); + + if (isNew) { + valueItem->setText(""); // Erase the "0" in new numbers + table->selectRow(rowIndex); + } +} + +bool CustomAttributesTable::deleteSelectedAttributes(QTableWidget * table) { + if (!table) + return false; + + int rowCount = table->rowCount(); + if (rowCount <= 0) + return false; + + QModelIndexList indexList = table->selectionModel()->selectedIndexes(); + QList persistentIndexes; + for (QModelIndex index : indexList) { + QPersistentModelIndex persistentIndex(index); + persistentIndexes.append(persistentIndex); + } + + for (QPersistentModelIndex index : persistentIndexes) { + table->removeRow(index.row()); + } + + if (table->rowCount() > 0) { + table->selectRow(0); + } + return true; +} diff --git a/src/ui/draggablepixmapitem.cpp b/src/ui/draggablepixmapitem.cpp index ef749a07..9e7ab855 100644 --- a/src/ui/draggablepixmapitem.cpp +++ b/src/ui/draggablepixmapitem.cpp @@ -85,23 +85,17 @@ void DraggablePixmapItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) { if (eventType == Event::Type::Warp) { WarpEvent *warp = dynamic_cast(this->event); QString destMap = warp->getDestinationMap(); - if (destMap != NONE_MAP_NAME) { - emit editor->warpEventDoubleClicked(destMap, warp->getDestinationWarpID(), Event::Group::Warp); - } + int warpId = ParseUtil::gameStringToInt(warp->getDestinationWarpID()); + emit editor->warpEventDoubleClicked(destMap, warpId, Event::Group::Warp); } else if (eventType == Event::Type::CloneObject) { CloneObjectEvent *clone = dynamic_cast(this->event); - QString destMap = clone->getTargetMap(); - if (destMap != NONE_MAP_NAME) { - emit editor->warpEventDoubleClicked(destMap, clone->getTargetID(), Event::Group::Object); - } + emit editor->warpEventDoubleClicked(clone->getTargetMap(), clone->getTargetID(), Event::Group::Object); } else if (eventType == Event::Type::SecretBase) { SecretBaseEvent *base = dynamic_cast(this->event); QString baseId = base->getBaseID(); QString destMap = editor->project->mapConstantsToMapNames.value("MAP_" + baseId.left(baseId.lastIndexOf("_"))); - if (destMap != NONE_MAP_NAME) { - emit editor->warpEventDoubleClicked(destMap, 0, Event::Group::Warp); - } + emit editor->warpEventDoubleClicked(destMap, 0, Event::Group::Warp); } } diff --git a/src/ui/eventframes.cpp b/src/ui/eventframes.cpp index c3f254c1..c90f3934 100644 --- a/src/ui/eventframes.cpp +++ b/src/ui/eventframes.cpp @@ -507,9 +507,9 @@ void WarpFrame::setup() { // desination warp id QFormLayout *l_form_dest_warp = new QFormLayout(); - this->spinner_dest_warp = new NoScrollSpinBox(this); - this->spinner_dest_warp->setToolTip("The warp id on the destination map."); - l_form_dest_warp->addRow("Destination Warp", this->spinner_dest_warp); + this->combo_dest_warp = new NoScrollComboBox(this); + this->combo_dest_warp->setToolTip("The warp id on the destination map."); + l_form_dest_warp->addRow("Destination Warp", this->combo_dest_warp); this->layout_contents->addLayout(l_form_dest_warp); // custom attributes @@ -529,9 +529,9 @@ void WarpFrame::connectSignals() { }); // dest id - this->spinner_dest_warp->disconnect(); - connect(this->spinner_dest_warp, QOverload::of(&QSpinBox::valueChanged), [this](int value) { - this->warp->setDestinationWarpID(value); + this->combo_dest_warp->disconnect(); + connect(this->combo_dest_warp, &QComboBox::currentTextChanged, [this](const QString &text) { + this->warp->setDestinationWarpID(text); this->warp->modify(); }); } @@ -553,7 +553,7 @@ void WarpFrame::initialize() { } // dest id - this->spinner_dest_warp->setValue(this->warp->getDestinationWarpID()); + this->combo_dest_warp->setCurrentText(this->warp->getDestinationWarpID()); } void WarpFrame::populate(Project *project) { diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 7f2d0581..5c5e4b78 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -241,7 +241,7 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress, bool inclu continue; int x = cur.x; int y = cur.y; - int offset = connection->offset.toInt(nullptr, 0); + int offset = connection->offset; Map *connectionMap = this->editor->project->loadMap(connection->map_name); if (connection->direction == "up") { x += offset; diff --git a/src/ui/newmappopup.cpp b/src/ui/newmappopup.cpp index 1450d9c8..1ffabf68 100644 --- a/src/ui/newmappopup.cpp +++ b/src/ui/newmappopup.cpp @@ -117,8 +117,8 @@ void NewMapPopup::setDefaultValues(int groupNum, QString mapSec) { ui->comboBox_Song->addItems(project->songNames); if (existingLayout) { - ui->spinBox_NewMap_Width->setValue(project->mapLayouts.value(layoutId)->width.toInt(nullptr, 0)); - ui->spinBox_NewMap_Height->setValue(project->mapLayouts.value(layoutId)->height.toInt(nullptr, 0)); + ui->spinBox_NewMap_Width->setValue(project->mapLayouts.value(layoutId)->width); + ui->spinBox_NewMap_Height->setValue(project->mapLayouts.value(layoutId)->height); ui->comboBox_NewMap_Primary_Tileset->setCurrentText(project->mapLayouts.value(layoutId)->tileset_primary_label); ui->comboBox_NewMap_Secondary_Tileset->setCurrentText(project->mapLayouts.value(layoutId)->tileset_secondary_label); ui->spinBox_NewMap_Width->setDisabled(true); @@ -155,8 +155,8 @@ void NewMapPopup::setDefaultValuesImportMap(MapLayout *mapLayout) { ui->comboBox_Song->addItems(project->songNames); - ui->spinBox_NewMap_Width->setValue(mapLayout->width.toInt(nullptr, 0)); - ui->spinBox_NewMap_Height->setValue(mapLayout->height.toInt(nullptr, 0)); + ui->spinBox_NewMap_Width->setValue(mapLayout->width); + ui->spinBox_NewMap_Height->setValue(mapLayout->height); ui->comboBox_NewMap_Primary_Tileset->setCurrentText(mapLayout->tileset_primary_label); ui->comboBox_NewMap_Secondary_Tileset->setCurrentText(mapLayout->tileset_secondary_label); @@ -207,8 +207,8 @@ void NewMapPopup::setDefaultValuesProjectConfig(bool importedMap, MapLayout *map } if (projectConfig.getUseCustomBorderSize()) { if (importedMap) { - ui->spinBox_NewMap_BorderWidth->setValue(mapLayout->border_width.toInt(nullptr, 0)); - ui->spinBox_NewMap_BorderHeight->setValue(mapLayout->border_height.toInt(nullptr, 0)); + ui->spinBox_NewMap_BorderWidth->setValue(mapLayout->border_width); + ui->spinBox_NewMap_BorderHeight->setValue(mapLayout->border_height); } ui->spinBox_NewMap_BorderWidth->setVisible(true); ui->spinBox_NewMap_BorderHeight->setVisible(true); @@ -265,9 +265,9 @@ void NewMapPopup::on_pushButton_NewMap_Accept_clicked() { newMap->type = this->ui->comboBox_NewMap_Type->currentText(); newMap->location = this->ui->comboBox_NewMap_Location->currentText(); newMap->song = this->ui->comboBox_Song->currentText(); - newMap->requiresFlash = "0"; + newMap->requiresFlash = false; newMap->weather = this->project->weatherNames.value(0, "WEATHER_NONE"); - newMap->show_location = this->ui->checkBox_NewMap_Show_Location->isChecked() ? "1" : "0"; + newMap->show_location = this->ui->checkBox_NewMap_Show_Location->isChecked(); newMap->battle_scene = this->project->mapBattleScenes.value(0, "MAP_BATTLE_SCENE_NORMAL"); if (this->existingLayout) { @@ -277,14 +277,14 @@ void NewMapPopup::on_pushButton_NewMap_Accept_clicked() { layout = new MapLayout; layout->id = MapLayout::layoutConstantFromName(newMapName); layout->name = QString("%1_Layout").arg(newMap->name); - layout->width = QString::number(this->ui->spinBox_NewMap_Width->value()); - layout->height = QString::number(this->ui->spinBox_NewMap_Height->value()); + layout->width = this->ui->spinBox_NewMap_Width->value(); + layout->height = this->ui->spinBox_NewMap_Height->value(); if (projectConfig.getUseCustomBorderSize()) { - layout->border_width = QString::number(this->ui->spinBox_NewMap_BorderWidth->value()); - layout->border_height = QString::number(this->ui->spinBox_NewMap_BorderHeight->value()); + layout->border_width = this->ui->spinBox_NewMap_BorderWidth->value(); + layout->border_height = this->ui->spinBox_NewMap_BorderHeight->value(); } else { - layout->border_width = QString::number(DEFAULT_BORDER_WIDTH); - layout->border_height = QString::number(DEFAULT_BORDER_HEIGHT); + layout->border_width = DEFAULT_BORDER_WIDTH; + layout->border_height = DEFAULT_BORDER_HEIGHT; } layout->tileset_primary_label = this->ui->comboBox_NewMap_Primary_Tileset->currentText(); layout->tileset_secondary_label = this->ui->comboBox_NewMap_Secondary_Tileset->currentText(); @@ -300,13 +300,13 @@ void NewMapPopup::on_pushButton_NewMap_Accept_clicked() { } if (this->ui->checkBox_NewMap_Flyable->isChecked()) { - newMap->isFlyable = "TRUE"; + newMap->needsHealLocation = true; } if (projectConfig.getBaseGameVersion() != BaseGameVersion::pokeruby) { - newMap->allowRunning = this->ui->checkBox_NewMap_Allow_Running->isChecked() ? "1" : "0"; - newMap->allowBiking = this->ui->checkBox_NewMap_Allow_Biking->isChecked() ? "1" : "0"; - newMap->allowEscapeRope = this->ui->checkBox_NewMap_Allow_Escape_Rope->isChecked() ? "1" : "0"; + newMap->allowRunning = this->ui->checkBox_NewMap_Allow_Running->isChecked(); + newMap->allowBiking = this->ui->checkBox_NewMap_Allow_Biking->isChecked(); + newMap->allowEscaping = this->ui->checkBox_NewMap_Allow_Escape_Rope->isChecked(); } if (projectConfig.getFloorNumberEnabled()) { newMap->floorNumber = this->ui->spinBox_NewMap_Floor_Number->value(); diff --git a/src/ui/prefab.cpp b/src/ui/prefab.cpp index e9e3b3b0..87bae518 100644 --- a/src/ui/prefab.cpp +++ b/src/ui/prefab.cpp @@ -46,14 +46,14 @@ void Prefab::loadPrefabs() { if (prefabObj.isEmpty()) continue; - int width = prefabObj["width"].toInt(); - int height = prefabObj["height"].toInt(); + int width = ParseUtil::jsonToInt(prefabObj["width"]); + int height = ParseUtil::jsonToInt(prefabObj["height"]); if (width <= 0 || height <= 0) continue; - QString name = prefabObj["name"].toString(); - QString primaryTileset = prefabObj["primary_tileset"].toString(); - QString secondaryTileset = prefabObj["secondary_tileset"].toString(); + QString name = ParseUtil::jsonToQString(prefabObj["name"]); + QString primaryTileset = ParseUtil::jsonToQString(prefabObj["primary_tileset"]); + QString secondaryTileset = ParseUtil::jsonToQString(prefabObj["secondary_tileset"]); MetatileSelection selection; selection.dimensions = QPoint(width, height); @@ -65,17 +65,17 @@ void Prefab::loadPrefabs() { 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(); + int x = ParseUtil::jsonToInt(metatileObj["x"]); + int y = ParseUtil::jsonToInt(metatileObj["y"]); if (x < 0 || x >= width || y < 0 || y >= height) continue; int index = y * width + x; - int metatileId = metatileObj["metatile_id"].toInt(); + int metatileId = ParseUtil::jsonToInt(metatileObj["metatile_id"]); 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].metatileId = ParseUtil::jsonToInt(metatileObj["metatile_id"]); + selection.collisionItems[index].collision = ParseUtil::jsonToInt(metatileObj["collision"]); + selection.collisionItems[index].elevation = ParseUtil::jsonToInt(metatileObj["elevation"]); selection.metatileItems[index].enabled = true; selection.collisionItems[index].enabled = true; } diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp index dc2c458e..8257231c 100644 --- a/src/ui/regionmapeditor.cpp +++ b/src/ui/regionmapeditor.cpp @@ -125,13 +125,13 @@ bool RegionMapEditor::loadRegionMapEntries() { for (auto entryRef : object["map_sections"].toArray()) { QJsonObject entryObject = entryRef.toObject(); - QString entryMapSection = entryObject["map_section"].toString(); + QString entryMapSection = ParseUtil::jsonToQString(entryObject["map_section"]); MapSectionEntry entry; - entry.name = entryObject["name"].toString(); - entry.x = entryObject["x"].toInt(); - entry.y = entryObject["y"].toInt(); - entry.width = entryObject["width"].toInt(); - entry.height = entryObject["height"].toInt(); + entry.name = ParseUtil::jsonToQString(entryObject["name"]); + entry.x = ParseUtil::jsonToInt(entryObject["x"]); + entry.y = ParseUtil::jsonToInt(entryObject["y"]); + entry.width = ParseUtil::jsonToInt(entryObject["width"]); + entry.height = ParseUtil::jsonToInt(entryObject["height"]); entry.valid = true; this->region_map_entries[entryMapSection] = entry; }