From d448765d631940ba094f15293cac53dc97b4ca2e Mon Sep 17 00:00:00 2001 From: GriffinR Date: Mon, 4 Nov 2024 21:49:49 -0500 Subject: [PATCH] Read/write MAPSEC values using the region map json --- docsrc/manual/project-files.rst | 2 - include/config.h | 2 - include/core/regionmap.h | 10 +- include/core/regionmapeditcommands.h | 4 +- include/core/wildmoninfo.h | 2 +- include/project.h | 13 +-- include/ui/maplistmodels.h | 2 +- include/ui/regionmapeditor.h | 3 +- src/config.cpp | 2 - src/core/regionmap.cpp | 37 +----- src/core/regionmapeditcommands.cpp | 2 +- src/mainwindow.cpp | 4 +- src/project.cpp | 169 +++++++++++++++------------ src/scriptapi/apimap.cpp | 2 +- src/scriptapi/apiutility.cpp | 2 +- src/ui/maplistmodels.cpp | 34 ++---- src/ui/newmappopup.cpp | 4 +- src/ui/regionmapeditor.cpp | 90 ++------------ 18 files changed, 143 insertions(+), 241 deletions(-) diff --git a/docsrc/manual/project-files.rst b/docsrc/manual/project-files.rst index 30f0acc8..e4f0479b 100644 --- a/docsrc/manual/project-files.rst +++ b/docsrc/manual/project-files.rst @@ -59,7 +59,6 @@ The filepath that Porymap expects for each file can be overridden on the ``Files include/constants/event_object_movement.h, yes, no, ``constants_obj_event_movement``, include/constants/event_objects.h, yes, no, ``constants_obj_events``, include/constants/event_bg.h, yes, no, ``constants_event_bg``, - include/constants/region_map_sections.h, yes, no, ``constants_region_map_sections``, include/constants/metatile_labels.h, yes, yes, ``constants_metatile_labels``, include/constants/metatile_behaviors.h, yes, no, ``constants_metatile_behaviors``, include/constants/species.h, yes, no, ``constants_metatile_behaviors``, for the Wild Pokémon tab @@ -122,7 +121,6 @@ In addition to these files, there are some specific symbol and macro names that ``define_map_empty``, ``UNDEFINED``, macro name after prefix for empty maps ``define_map_section_prefix``, ``MAPSEC_``, expected prefix for location macro names ``define_map_section_empty``, ``NONE``, macro name after prefix for empty region map sections - ``define_map_section_count``, ``COUNT``, macro name after prefix for total number of region map sections ``define_species_prefix``, ``SPECIES_``, expected prefix for species macro names ``regex_behaviors``, ``\bMB_``, regex to find metatile behavior macro names ``regex_obj_event_gfx``, ``\bOBJ_EVENT_GFX_``, regex to find Object Event graphics ID macro names diff --git a/include/config.h b/include/config.h index 31f7fa7f..6b64b612 100644 --- a/include/config.h +++ b/include/config.h @@ -213,7 +213,6 @@ enum ProjectIdentifier { define_map_empty, define_map_section_prefix, define_map_section_empty, - define_map_section_count, define_species_prefix, regex_behaviors, regex_obj_event_gfx, @@ -269,7 +268,6 @@ enum ProjectFilePath { constants_obj_event_movement, constants_obj_events, constants_event_bg, - constants_region_map_sections, constants_metatile_labels, constants_metatile_behaviors, constants_species, diff --git a/include/core/regionmap.h b/include/core/regionmap.h index bc05a84f..822df79b 100644 --- a/include/core/regionmap.h +++ b/include/core/regionmap.h @@ -57,8 +57,8 @@ public: bool loadLayout(poryjson::Json); bool loadEntries(); - void setEntries(tsl::ordered_map *entries) { this->region_map_entries = entries; } - void setEntries(tsl::ordered_map entries) { *(this->region_map_entries) = entries; } + void setEntries(QMap *entries) { this->region_map_entries = entries; } + void setEntries(const QMap &entries) { *(this->region_map_entries) = entries; } void clearEntries() { this->region_map_entries->clear(); } MapSectionEntry getEntry(QString section); void setEntry(QString section, MapSectionEntry entry); @@ -114,8 +114,6 @@ public: void setLayer(QString layer) { this->current_layer = layer; } QString getLayer() { return this->current_layer; } - QString fixCase(QString); - int padLeft() { return this->offset_left; } int padTop() { return this->offset_top; } int padRight() { return this->tilemap_width - this->layout_width - this->offset_left; } @@ -149,14 +147,12 @@ public: const QString section_prefix; const QString default_map_section; - const QString count_map_section; signals: void mapNeedsDisplaying(); private: - // TODO: defaults needed? - tsl::ordered_map *region_map_entries = nullptr; + QMap *region_map_entries = nullptr; QString alias = ""; diff --git a/include/core/regionmapeditcommands.h b/include/core/regionmapeditcommands.h index e142c5cc..05b12bc3 100644 --- a/include/core/regionmapeditcommands.h +++ b/include/core/regionmapeditcommands.h @@ -153,7 +153,7 @@ private: /// ClearEntries class ClearEntries : public QUndoCommand { public: - ClearEntries(RegionMap *map, tsl::ordered_map, QUndoCommand *parent = nullptr); + ClearEntries(RegionMap *map, QMap, QUndoCommand *parent = nullptr); void undo() override; void redo() override; @@ -163,7 +163,7 @@ public: private: RegionMap *map; - tsl::ordered_map entries; + QMap entries; }; #endif // REGIONMAPEDITCOMMANDS_H diff --git a/include/core/wildmoninfo.h b/include/core/wildmoninfo.h index 6c93d0d2..d0aa29dc 100644 --- a/include/core/wildmoninfo.h +++ b/include/core/wildmoninfo.h @@ -8,7 +8,7 @@ struct WildPokemon { int minLevel = 5; int maxLevel = 5; - QString species = "SPECIES_NONE"; + QString species = "SPECIES_NONE"; // TODO: Use define_species_prefix }; struct WildMonInfo { diff --git a/include/project.h b/include/project.h index 39c492bb..c99bd05b 100644 --- a/include/project.h +++ b/include/project.h @@ -49,9 +49,6 @@ public: QMap layoutIdsToNames; QMap mapLayouts; QMap mapLayoutsMaster; - QMap mapSecToMapHoverName; - QMap mapSectionNameToValue; - QMap mapSectionValueToName; QMap eventGraphicsMap; QMap gfxDefines; QString defaultSong; @@ -68,6 +65,8 @@ public: QStringList bgEventFacingDirections; QStringList trainerTypes; QStringList globalScriptLabels; + QStringList mapSectionIdNames; + QMap regionMapEntries; QMap> metatileLabelsMap; QMap unusedMetatileLabels; QMap metatileBehaviorMap; @@ -82,9 +81,7 @@ public: int pokemonMaxLevel; int maxEncounterRate; bool wildEncountersLoaded; - - // For files that are read and could contain extra text - QMap extraFileText; + bool saveEmptyMapsec; void set_root(QString); @@ -142,7 +139,7 @@ public: bool readSpeciesIconPaths(); QMap speciesToIconPath; - int appendMapsec(QString name); + void addNewMapsec(QString name); bool hasUnsavedChanges(); bool hasUnsavedDataChanges = false; @@ -172,7 +169,7 @@ public: void saveConfig(); void saveMapLayouts(); void saveMapGroups(); - void saveMapSections(); + void saveRegionMapSections(); void saveWildMonData(); void saveMapConstantsHeader(); void saveHealLocations(Map*); diff --git a/include/ui/maplistmodels.h b/include/ui/maplistmodels.h index 20eb24a9..3e6e95d1 100644 --- a/include/ui/maplistmodels.h +++ b/include/ui/maplistmodels.h @@ -132,7 +132,7 @@ public: public: void setMap(QString mapName) { this->openMap = mapName; } - QStandardItem *createAreaItem(QString areaName, int areaIndex); + QStandardItem *createAreaItem(QString areaName); QStandardItem *createMapItem(QString mapName, int areaIndex, int mapIndex); QStandardItem *insertAreaItem(QString areaName); diff --git a/include/ui/regionmapeditor.h b/include/ui/regionmapeditor.h index 3d889f88..c1941651 100644 --- a/include/ui/regionmapeditor.h +++ b/include/ui/regionmapeditor.h @@ -57,7 +57,6 @@ private: tsl::ordered_map region_maps; QString configFilepath; - QString mapSectionFilepath; poryjson::Json rmConfigJson; @@ -96,7 +95,7 @@ private: void saveConfig(); bool loadRegionMapEntries(); bool saveRegionMapEntries(); - tsl::ordered_map region_map_entries; + QMap region_map_entries; bool buildConfigDialog(); poryjson::Json configRegionMapDialog(); diff --git a/src/config.cpp b/src/config.cpp index ca6473b1..3d604da6 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -110,7 +110,6 @@ const QMap> ProjectConfig::defaultIde {ProjectIdentifier::define_map_empty, {"define_map_empty", "UNDEFINED"}}, {ProjectIdentifier::define_map_section_prefix, {"define_map_section_prefix", "MAPSEC_"}}, {ProjectIdentifier::define_map_section_empty, {"define_map_section_empty", "NONE"}}, - {ProjectIdentifier::define_map_section_count, {"define_map_section_count", "COUNT"}}, {ProjectIdentifier::define_species_prefix, {"define_species_prefix", "SPECIES_"}}, // Regex {ProjectIdentifier::regex_behaviors, {"regex_behaviors", "\\bMB_"}}, @@ -167,7 +166,6 @@ const QMap> ProjectConfig::defaultPaths {ProjectFilePath::constants_obj_event_movement, { "constants_obj_event_movement", "include/constants/event_object_movement.h"}}, {ProjectFilePath::constants_obj_events, { "constants_obj_events", "include/constants/event_objects.h"}}, {ProjectFilePath::constants_event_bg, { "constants_event_bg", "include/constants/event_bg.h"}}, - {ProjectFilePath::constants_region_map_sections, { "constants_region_map_sections", "include/constants/region_map_sections.h"}}, {ProjectFilePath::constants_metatile_labels, { "constants_metatile_labels", "include/constants/metatile_labels.h"}}, {ProjectFilePath::constants_metatile_behaviors, { "constants_metatile_behaviors", "include/constants/metatile_behaviors.h"}}, {ProjectFilePath::constants_species, { "constants_species", "include/constants/species.h"}}, diff --git a/src/core/regionmap.cpp b/src/core/regionmap.cpp index 7dbd4c5f..94650ccd 100644 --- a/src/core/regionmap.cpp +++ b/src/core/regionmap.cpp @@ -19,8 +19,7 @@ using std::make_shared; RegionMap::RegionMap(Project *project) : section_prefix(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix)), - default_map_section(section_prefix + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_empty)), - count_map_section(section_prefix + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_count)) + default_map_section(project->getEmptyMapsecName()) { this->project = project; } @@ -157,7 +156,7 @@ bool RegionMap::loadLayout(poryjson::Json layoutJson) { for (int x = 0; x < this->layout_width; x++) { int bin_index = x + y * this->layout_width; uint8_t square_section_id = mapBinData.at(bin_index); - QString square_section_name = project->mapSectionValueToName.value(square_section_id); + QString square_section_name = project->mapSectionIdNames.value(square_section_id, this->default_map_section); LayoutSquare square; square.map_section = square_section_name; @@ -401,7 +400,7 @@ void RegionMap::saveLayout() { for (int m = 0; m < this->layout_height; m++) { for (int n = 0; n < this->layout_width; n++) { int i = n + this->layout_width * m; - data.append(this->project->mapSectionNameToValue.value(this->layouts["main"][i].map_section)); + data.append(this->project->mapSectionIdNames.indexOf(this->layouts["main"][i].map_section)); } } QFile bfile(fullPath(this->layout_path)); @@ -760,18 +759,15 @@ bool RegionMap::squareInLayout(int x, int y) { } MapSectionEntry RegionMap::getEntry(QString section) { - if (this->region_map_entries->contains(section)) - return this->region_map_entries->operator[](section); - else - return MapSectionEntry(); + return this->region_map_entries->value(section, MapSectionEntry()); } void RegionMap::setEntry(QString section, MapSectionEntry entry) { - this->region_map_entries->operator[](section) = entry; + this->region_map_entries->insert(section, entry); } void RegionMap::removeEntry(QString section) { - this->region_map_entries->erase(section); + this->region_map_entries->remove(section); } QString RegionMap::palPath() { @@ -788,27 +784,6 @@ int RegionMap::getMapSquareIndex(int x, int y) { return ((index < tilemap.length()) && (index >= 0)) ? index : 0; } -// For turning a MAPSEC_NAME into a unique identifier sMapName-style variable. -// CAPS_WITH_UNDERSCORE to CamelCase -QString RegionMap::fixCase(QString caps) { - bool big = true; - QString camel; - - static const QRegularExpression re_braced("({.*})"); - for (auto ch : caps.remove(re_braced).remove(this->section_prefix)) { - if (ch == '_' || ch == ' ') { - big = true; - continue; - } - if (big) { - camel += ch.toUpper(); - big = false; - } - else camel += ch.toLower(); - } - return camel; -} - QString RegionMap::fullPath(QString local) { return this->project->root + "/" + local; } diff --git a/src/core/regionmapeditcommands.cpp b/src/core/regionmapeditcommands.cpp index 1be247b0..7a12cbc6 100644 --- a/src/core/regionmapeditcommands.cpp +++ b/src/core/regionmapeditcommands.cpp @@ -260,7 +260,7 @@ void ResizeTilemap::undo() { /// -ClearEntries::ClearEntries(RegionMap *map, tsl::ordered_map entries, QUndoCommand *parent) +ClearEntries::ClearEntries(RegionMap *map, QMap entries, QUndoCommand *parent) : QUndoCommand(parent) { setText("Clear Entries"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0ace9fb9..90b2a80c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1177,7 +1177,7 @@ bool MainWindow::setProjectUI() { ui->comboBox_Song->clear(); ui->comboBox_Song->addItems(project->songNames); ui->comboBox_Location->clear(); - ui->comboBox_Location->addItems(project->mapSectionValueToName.values()); + ui->comboBox_Location->addItems(project->mapSectionIdNames); ui->comboBox_PrimaryTileset->clear(); ui->comboBox_PrimaryTileset->addItems(project->primaryTilesetLabels); ui->comboBox_SecondaryTileset->clear(); @@ -1546,7 +1546,7 @@ void MainWindow::mapListAddArea() { connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){ const QString newAreaName = newNameDisplay->text(); - if (this->editor->project->mapSectionNameToValue.contains(newAreaName)){ + if (this->editor->project->mapSectionIdNames.contains(newAreaName)){ errorMessageLabel->setText(QString("An area with the name '%1' already exists").arg(newAreaName)); errorMessageLabel->setVisible(true); } else { diff --git a/src/project.cpp b/src/project.cpp index 657bd89c..9d87c6f4 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -708,36 +708,45 @@ void Project::saveMapGroups() { mapGroupsFile.close(); } -void Project::saveMapSections() { - QString filepath = root + "/" + projectConfig.getFilePath(ProjectFilePath::constants_region_map_sections); - - QString text = QString("#ifndef GUARD_REGIONMAPSEC_H\n"); - text += QString("#define GUARD_REGIONMAPSEC_H\n\n"); - - int longestLength = 0; - for (QString label : this->mapSectionNameToValue.keys()) { - if (label.size() > longestLength) - longestLength = label.size(); +void Project::saveRegionMapSections() { + const QString filepath = QString("%1/%2").arg(this->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_map_entries)); + QFile file(filepath); + if (!file.open(QIODevice::WriteOnly)) { + logError(QString("Could not open '%1' for writing").arg(filepath)); + return; } - longestLength += 1; + const QString emptyMapsecName = getEmptyMapsecName(); + OrderedJson::array mapSectionArray; + for (const auto &idName : this->mapSectionIdNames) { + // The 'empty' map section (MAPSEC_NONE) isn't normally present in the region map sections data file. + // We append this name to mapSectionIdNames ourselves if it isn't present, in which case we don't want to output data for it here. + if (!this->saveEmptyMapsec && idName == emptyMapsecName) + continue; - // TODO: Maybe print as an enum now that we can? - for (int value : this->mapSectionValueToName.keys()) { - QString line = QString("#define %1 0x%2\n") - .arg(this->mapSectionValueToName[value], -1 * longestLength) - .arg(QString("%1").arg(value, 2, 16, QLatin1Char('0')).toUpper()); - text += line; + OrderedJson::object mapSectionObj; + mapSectionObj["id"] = idName; + + if (this->regionMapEntries.contains(idName)) { + MapSectionEntry entry = this->regionMapEntries.value(idName); + mapSectionObj["name"] = entry.name; + mapSectionObj["x"] = entry.x; + mapSectionObj["y"] = entry.y; + mapSectionObj["width"] = entry.width; + mapSectionObj["height"] = entry.height; + } + + mapSectionArray.append(mapSectionObj); } - // TODO: We should maybe consider another way to update MAPSEC values in this file, in case we break anything by relocating it to the bottom of the file. - // (or alternatively keep separate strings for text before/after the MAPSEC values) - text += "\n" + this->extraFileText[projectConfig.getFilePath(ProjectFilePath::constants_region_map_sections)] + "\n"; - - text += QString("#endif // GUARD_REGIONMAPSEC_H\n"); + OrderedJson::object object; + object["map_sections"] = mapSectionArray; ignoreWatchedFileTemporarily(filepath); - saveTextFile(filepath, text); + OrderedJson json(object); + OrderedJsonDoc jsonDoc(&json); + jsonDoc.dump(&file); + file.close(); } void Project::saveWildMonData() { @@ -1459,7 +1468,7 @@ void Project::updateLayout(Layout *layout) { void Project::saveAllDataStructures() { saveMapLayouts(); saveMapGroups(); - saveMapSections(); + saveRegionMapSections(); saveMapConstantsHeader(); saveWildMonData(); saveConfig(); @@ -2242,49 +2251,66 @@ bool Project::readFieldmapMasks() { } bool Project::readRegionMapSections() { - this->mapSectionNameToValue.clear(); - this->mapSectionValueToName.clear(); + this->mapSectionIdNames.clear(); + this->regionMapEntries.clear(); + this->saveEmptyMapsec = false; + const QString defaultName = getEmptyMapsecName(); + const QString requiredPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix); - const QStringList regexList = {QString("\\b%1").arg(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix))}; - QString filename = projectConfig.getFilePath(ProjectFilePath::constants_region_map_sections); - fileWatcher.addPath(root + "/" + filename); - this->mapSectionNameToValue = parser.readCDefinesByRegex(filename, regexList); - if (this->mapSectionNameToValue.isEmpty()) { - logError(QString("Failed to read region map sections from %1.").arg(filename)); + QJsonDocument doc; + const QString filepath = QString("%1/%2").arg(this->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_map_entries)); + if (!parser.tryParseJsonFile(&doc, filepath)) { + logError(QString("Failed to read region map sections from '%1'").arg(filepath)); return false; } + fileWatcher.addPath(filepath); - for (QString defineName : this->mapSectionNameToValue.keys()) { - this->mapSectionValueToName.insert(this->mapSectionNameToValue[defineName], defineName); + QJsonArray mapSections = doc.object()["map_sections"].toArray(); + for (const auto &mapSection : mapSections) { + // For each map section, "id" is the only required field. This is the field we use + // to display the location names in various drop-downs. + QJsonObject mapSectionObj = mapSection.toObject(); + const QString idName = ParseUtil::jsonToQString(mapSectionObj["id"]); + if (!idName.startsWith(requiredPrefix)) { + logWarn(QString("Ignoring data for map section '%1'. IDs must start with the prefix '%2'").arg(idName).arg(requiredPrefix)); + continue; + } + + this->mapSectionIdNames.append(idName); + if (idName == defaultName) { + // If the user has data for the 'empty' MAPSEC we need to know to output it later, + // because we will otherwise add a dummy entry for this value. + this->saveEmptyMapsec = true; + } + + // Map sections may have additional data indicating their position on the region map. + // If they have this data, we can add them to the region map entry list. + bool hasRegionMapData = true; + static const QSet regionMapFieldNames = { "name", "x", "y", "width", "height" }; + for (auto fieldName : regionMapFieldNames) { + if (!mapSectionObj.contains(fieldName)) { + hasRegionMapData = false; + break; + } + } + if (!hasRegionMapData) + continue; + + MapSectionEntry entry; + entry.name = ParseUtil::jsonToQString(mapSectionObj["name"]); + entry.x = ParseUtil::jsonToInt(mapSectionObj["x"]); + entry.y = ParseUtil::jsonToInt(mapSectionObj["y"]); + entry.width = ParseUtil::jsonToInt(mapSectionObj["width"]); + entry.height = ParseUtil::jsonToInt(mapSectionObj["height"]); + entry.valid = true; + this->regionMapEntries[idName] = entry; } - // extra text - QString extraText; - QString fileText = ParseUtil::readTextFile(root + "/" + filename); - QTextStream stream(&fileText); - QString currentLine; - while (stream.readLineInto(¤tLine)) { - // is this line something that porymap will output again? - if (currentLine.isEmpty()) { - continue; - } - // include guards - // TODO: Assuming guard name is the same across projects (it isn't) - else if (currentLine.contains("GUARD_REGIONMAPSEC_H")) { - continue; - } - // defines captured - // TODO: Regex to consider comments/extra space - else if (currentLine.contains("#define " + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix))) { - continue; - } - // everything else should be kept here - else { - extraText += currentLine + "\n"; - } + // Make sure the default name is present in the list. + if (!this->mapSectionIdNames.contains(defaultName)) { + this->mapSectionIdNames.append(defaultName); } - stream.seek(0); - this->extraFileText[filename] = extraText; + return true; } @@ -2292,24 +2318,15 @@ QString Project::getEmptyMapsecName() { return projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix) + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_empty); } -// This function assumes a valid and unique name. -// Will return the new index. -int Project::appendMapsec(QString name) { - const QString emptyMapsecName = getEmptyMapsecName(); - int newMapsecValue = mapSectionValueToName.isEmpty() ? 0 : mapSectionValueToName.lastKey(); - - // If the user has the 'empty' MAPSEC value defined last in the list we'll shift it so that it stays last in the list. - if (this->mapSectionNameToValue.contains(emptyMapsecName) && this->mapSectionNameToValue.value(emptyMapsecName) == newMapsecValue) { - this->mapSectionNameToValue.insert(emptyMapsecName, newMapsecValue + 1); - this->mapSectionValueToName.insert(newMapsecValue + 1, emptyMapsecName); +// This function assumes a valid and unique name +void Project::addNewMapsec(QString name) { + if (!this->mapSectionIdNames.isEmpty() && this->mapSectionIdNames.last() == getEmptyMapsecName()) { + // If the default map section name (MAPSEC_NONE) is last in the list we'll keep it last in the list. + this->mapSectionIdNames.insert(this->mapSectionIdNames.length() - 1, name); + } else { + this->mapSectionIdNames.append(name); } - - // TODO: Update 'define_map_section_count'? - - this->mapSectionNameToValue[name] = newMapsecValue; - this->mapSectionValueToName[newMapsecValue] = name; this->hasUnsavedDataChanges = true; - return newMapsecValue; } // Read the constants to preserve any "unused" heal locations when writing the file later diff --git a/src/scriptapi/apimap.cpp b/src/scriptapi/apimap.cpp index ef260390..98478f94 100644 --- a/src/scriptapi/apimap.cpp +++ b/src/scriptapi/apimap.cpp @@ -836,7 +836,7 @@ QString MainWindow::getLocation() { void MainWindow::setLocation(QString location) { if (!this->ui || !this->editor || !this->editor->project) return; - if (!this->editor->project->mapSectionNameToValue.contains(location)) { + if (!this->editor->project->mapSectionIdNames.contains(location)) { logError(QString("Unknown location '%1'").arg(location)); return; } diff --git a/src/scriptapi/apiutility.cpp b/src/scriptapi/apiutility.cpp index 27bd5677..e5cebc54 100644 --- a/src/scriptapi/apiutility.cpp +++ b/src/scriptapi/apiutility.cpp @@ -282,7 +282,7 @@ QList ScriptUtility::getSongNames() { QList ScriptUtility::getLocationNames() { if (!window || !window->editor || !window->editor->project) return QList(); - return window->editor->project->mapSectionNameToValue.keys(); + return window->editor->project->mapSectionIdNames; } QList ScriptUtility::getWeatherNames() { diff --git a/src/ui/maplistmodels.cpp b/src/ui/maplistmodels.cpp index e4ebd82c..616e608c 100644 --- a/src/ui/maplistmodels.cpp +++ b/src/ui/maplistmodels.cpp @@ -413,13 +413,12 @@ MapAreaModel::MapAreaModel(Project *project, QObject *parent) : MapListModel(par initialize(); } -QStandardItem *MapAreaModel::createAreaItem(QString mapsecName, int areaIndex) { +QStandardItem *MapAreaModel::createAreaItem(QString mapsecName) { QStandardItem *area = new QStandardItem; area->setText(mapsecName); area->setEditable(false); area->setData(mapsecName, Qt::UserRole); area->setData("map_section", MapListUserRoles::TypeRole); - area->setData(areaIndex, MapListUserRoles::GroupRole); // group->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); this->areaItems.insert(mapsecName, area); return area; @@ -437,20 +436,14 @@ QStandardItem *MapAreaModel::createMapItem(QString mapName, int, int) { } QStandardItem *MapAreaModel::insertAreaItem(QString areaName) { - int newAreaIndex = this->project->appendMapsec(areaName); - QStandardItem *item = createAreaItem(areaName, newAreaIndex); - this->root->insertRow(newAreaIndex, item); - - // MAPSEC_NONE may have shifted to accomodate the new item, update it in the list. - const QString emptyMapsecName = Project::getEmptyMapsecName(); - if (this->areaItems.contains(emptyMapsecName)) - this->areaItems[emptyMapsecName]->setData(this->project->mapSectionNameToValue.value(emptyMapsecName), MapListUserRoles::GroupRole); - + this->project->addNewMapsec(areaName); + QStandardItem *item = createAreaItem(areaName); + this->root->appendRow(item); + this->sort(0, Qt::AscendingOrder); return item; } QStandardItem *MapAreaModel::insertMapItem(QString mapName, QString areaName, int groupIndex) { - // int areaIndex = this->project->mapSectionNameToValue[areaName]; QStandardItem *area = this->areaItems[areaName]; if (!area) { return nullptr; @@ -461,21 +454,18 @@ QStandardItem *MapAreaModel::insertMapItem(QString mapName, QString areaName, in return map; } +// Note: Not actually supported in the interface at the moment. void MapAreaModel::removeFolder(int index) { this->removeRow(index); - this->project->mapSectionNameToValue.remove(this->project->mapSectionValueToName.take(index)); + this->project->mapSectionIdNames.removeAt(index); } void MapAreaModel::initialize() { this->areaItems.clear(); this->mapItems.clear(); - this->setSortRole(MapListUserRoles::GroupRole); - // TODO: Ignore 'define_map_section_count' and/or 'define_map_section_empty'? - for (int i : this->project->mapSectionNameToValue) { - QString mapsecName = project->mapSectionValueToName.value(i); - QStandardItem *areaItem = createAreaItem(mapsecName, i); - this->root->appendRow(areaItem); + for (const auto &idName : this->project->mapSectionIdNames) { + this->root->appendRow(createAreaItem(idName)); } for (int i = 0; i < this->project->groupNames.length(); i++) { @@ -560,9 +550,7 @@ QVariant MapAreaModel::data(const QModelIndex &index, int role) const { QString type = item->data(MapListUserRoles::TypeRole).toString(); if (type == "map_section") { - return QString("[0x%1] %2") - .arg(QString("%1").arg(item->data(MapListUserRoles::GroupRole).toInt(), 2, 16, QLatin1Char('0')).toUpper()) - .arg(item->data(Qt::UserRole).toString()); + return item->data(Qt::UserRole).toString(); } } @@ -603,6 +591,7 @@ QStandardItem *LayoutTreeModel::createMapItem(QString mapName) { QStandardItem *LayoutTreeModel::insertLayoutItem(QString layoutId) { QStandardItem *layoutItem = this->createLayoutItem(layoutId); this->root->appendRow(layoutItem); + this->sort(0, Qt::AscendingOrder); return layoutItem; } @@ -644,6 +633,7 @@ void LayoutTreeModel::initialize() { this->layoutItems[layoutId]->appendRow(map); } } + this->sort(0, Qt::AscendingOrder); } QStandardItem *LayoutTreeModel::getItem(const QModelIndex &index) const { diff --git a/src/ui/newmappopup.cpp b/src/ui/newmappopup.cpp index a9e50c93..261544f2 100644 --- a/src/ui/newmappopup.cpp +++ b/src/ui/newmappopup.cpp @@ -34,7 +34,7 @@ void NewMapPopup::initUi() { ui->comboBox_NewMap_Group->addItems(project->groupNames); ui->comboBox_NewMap_Song->addItems(project->songNames); ui->comboBox_NewMap_Type->addItems(project->mapTypes); - ui->comboBox_NewMap_Location->addItems(project->mapSectionNameToValue.keys()); + ui->comboBox_NewMap_Location->addItems(project->mapSectionIdNames); const QSignalBlocker b(ui->comboBox_Layout); ui->comboBox_Layout->addItems(project->mapLayoutsTable); @@ -186,7 +186,7 @@ void NewMapPopup::setDefaultSettings(Project *project) { settings.primaryTilesetLabel = project->getDefaultPrimaryTilesetLabel(); settings.secondaryTilesetLabel = project->getDefaultSecondaryTilesetLabel(); settings.type = project->mapTypes.value(0, "0"); - settings.location = project->mapSectionNameToValue.keys().value(0, "0"); + settings.location = project->mapSectionIdNames.value(0, "0"); settings.song = project->defaultSong; settings.canFlyTo = false; settings.showLocationName = true; diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp index 2a23b454..c6485d51 100644 --- a/src/ui/regionmapeditor.cpp +++ b/src/ui/regionmapeditor.cpp @@ -29,7 +29,6 @@ RegionMapEditor::RegionMapEditor(QWidget *parent, Project *project) : this->ui->setupUi(this); this->project = project; this->configFilepath = QString("%1/%2").arg(this->project->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_porymap_cfg)); - this->mapSectionFilepath = QString("%1/%2").arg(this->project->root).arg(projectConfig.getFilePath(ProjectFilePath::json_region_map_entries)); this->initShortcuts(); this->restoreWindowState(); } @@ -110,67 +109,13 @@ void RegionMapEditor::applyUserShortcuts() { } bool RegionMapEditor::loadRegionMapEntries() { - this->region_map_entries.clear(); - - ParseUtil parser; - QJsonDocument sectionsDoc; - if (!parser.tryParseJsonFile(§ionsDoc, this->mapSectionFilepath)) { - logError(QString("Failed to read map data from %1").arg(this->mapSectionFilepath)); - return false; - } - - // for some unknown reason, the OrderedJson class would not parse this properly - // perhaps updating nlohmann/json here would fix it, but that also requires using C++17 - QJsonObject object = sectionsDoc.object(); - - for (auto entryRef : object["map_sections"].toArray()) { - QJsonObject entryObject = entryRef.toObject(); - QString entryMapSection = ParseUtil::jsonToQString(entryObject["map_section"]); - MapSectionEntry entry; - 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; - } - + this->region_map_entries = this->project->regionMapEntries; return true; } bool RegionMapEditor::saveRegionMapEntries() { - QFile sectionsFile(this->mapSectionFilepath); - if (!sectionsFile.open(QIODevice::WriteOnly)) { - logError(QString("Could not open %1 for writing").arg(this->mapSectionFilepath)); - return false; - } - - OrderedJson::object object; - OrderedJson::array mapSectionArray; - - for (auto pair : this->region_map_entries) { - QString section = pair.first; - MapSectionEntry entry = pair.second; - - OrderedJson::object entryObject; - entryObject["map_section"] = section; - entryObject["name"] = entry.name; - entryObject["x"] = entry.x; - entryObject["y"] = entry.y; - entryObject["width"] = entry.width; - entryObject["height"] = entry.height; - - mapSectionArray.append(entryObject); - } - - object["map_sections"] = mapSectionArray; - - OrderedJson sectionsJson(object); - OrderedJsonDoc jsonDoc(§ionsJson); - jsonDoc.dump(§ionsFile); - sectionsFile.close(); - + this->project->regionMapEntries = this->region_map_entries; + this->project->saveRegionMapSections(); return true; } @@ -708,7 +653,7 @@ void RegionMapEditor::displayRegionMapLayoutOptions() { this->ui->comboBox_RM_ConnectedMap->blockSignals(true); this->ui->comboBox_RM_ConnectedMap->clear(); - this->ui->comboBox_RM_ConnectedMap->addItems(this->project->mapSectionValueToName.values()); + this->ui->comboBox_RM_ConnectedMap->addItems(this->project->mapSectionIdNames); this->ui->comboBox_RM_ConnectedMap->blockSignals(false); this->ui->frame_RM_Options->setEnabled(true); @@ -775,7 +720,7 @@ void RegionMapEditor::displayRegionMapEntryOptions() { if (!this->region_map->layoutEnabled()) return; this->ui->comboBox_RM_Entry_MapSection->clear(); - this->ui->comboBox_RM_Entry_MapSection->addItems(this->project->mapSectionValueToName.values()); + this->ui->comboBox_RM_Entry_MapSection->addItems(this->project->mapSectionIdNames); this->ui->spinBox_RM_Entry_x->setMaximum(128); this->ui->spinBox_RM_Entry_y->setMaximum(128); this->ui->spinBox_RM_Entry_width->setMinimum(1); @@ -787,17 +732,13 @@ void RegionMapEditor::displayRegionMapEntryOptions() { void RegionMapEditor::updateRegionMapEntryOptions(QString section) { if (!this->region_map->layoutEnabled()) return; - bool isSpecialSection = (section == this->region_map->default_map_section - || section == this->region_map->count_map_section); - - bool enabled = (!isSpecialSection && this->region_map_entries.contains(section)); - + bool enabled = (section != this->region_map->default_map_section) && this->region_map_entries.contains(section); this->ui->lineEdit_RM_MapName->setEnabled(enabled); this->ui->spinBox_RM_Entry_x->setEnabled(enabled); this->ui->spinBox_RM_Entry_y->setEnabled(enabled); this->ui->spinBox_RM_Entry_width->setEnabled(enabled); this->ui->spinBox_RM_Entry_height->setEnabled(enabled); - this->ui->pushButton_entryActivate->setEnabled(!isSpecialSection); + this->ui->pushButton_entryActivate->setEnabled(section != this->region_map->default_map_section); this->ui->pushButton_entryActivate->setText(enabled ? "Remove" : "Add"); this->ui->lineEdit_RM_MapName->blockSignals(true); @@ -902,14 +843,8 @@ void RegionMapEditor::onRegionMapEntryDragged(int new_x, int new_y) { } void RegionMapEditor::onRegionMapLayoutSelectedTileChanged(int index) { - QString message = QString(); this->currIndex = index; this->region_map_layout_item->highlightedTile = index; - if (this->region_map->squareHasMap(index)) { - message = QString("\t %1").arg(this->project->mapSecToMapHoverName.value( - this->region_map->squareMapSection(index))).remove("{NAME_END}"); - } - this->ui->statusbar->showMessage(message); updateRegionMapLayoutOptions(index); this->region_map_layout_item->draw(); @@ -922,8 +857,7 @@ void RegionMapEditor::onRegionMapLayoutHoveredTileChanged(int index) { if (x >= 0 && y >= 0) { message = QString("(%1, %2)").arg(x).arg(y); if (this->region_map->squareHasMap(index)) { - message += QString("\t %1").arg(this->project->mapSecToMapHoverName.value( - this->region_map->squareMapSection(index))).remove("{NAME_END}"); + message += QString("\t %1").arg(this->region_map->squareMapSection(index)); } } this->ui->statusbar->showMessage(message); @@ -1203,10 +1137,10 @@ void RegionMapEditor::on_action_Swap_triggered() { QFormLayout form(&popup); QComboBox *oldSecBox = new QComboBox(); - oldSecBox->addItems(this->project->mapSectionValueToName.values()); + oldSecBox->addItems(this->project->mapSectionIdNames); form.addRow(new QLabel("Map Section 1:"), oldSecBox); QComboBox *newSecBox = new QComboBox(); - newSecBox->addItems(this->project->mapSectionValueToName.values()); + newSecBox->addItems(this->project->mapSectionIdNames); form.addRow(new QLabel("Map Section 2:"), newSecBox); QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup); @@ -1242,10 +1176,10 @@ void RegionMapEditor::on_action_Replace_triggered() { QFormLayout form(&popup); QComboBox *oldSecBox = new QComboBox(); - oldSecBox->addItems(this->project->mapSectionValueToName.values()); + oldSecBox->addItems(this->project->mapSectionIdNames); form.addRow(new QLabel("Old Map Section:"), oldSecBox); QComboBox *newSecBox = new QComboBox(); - newSecBox->addItems(this->project->mapSectionValueToName.values()); + newSecBox->addItems(this->project->mapSectionIdNames); form.addRow(new QLabel("New Map Section:"), newSecBox); QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &popup);