diff --git a/docsrc/manual/settings-and-options.rst b/docsrc/manual/settings-and-options.rst index 2c231ea2..27608636 100644 --- a/docsrc/manual/settings-and-options.rst +++ b/docsrc/manual/settings-and-options.rst @@ -10,7 +10,11 @@ A global settings file is stored in a platform-dependent location for app config A config file is also created when opening a project in porymap for the first time. It is stored in your project root as ``porymap.project.cfg``. There are several project-specific settings that are -determined by this file. +determined by this file. You may want to check in this file so that other users will have your +porject settings. + +A second config file is created for user-specific settings. It is stored in +your project root as ``porymap.user.cfg``. You should add this file to your gitignore. .. csv-table:: :header: Setting,Default,Location,Can Edit?,Description @@ -18,7 +22,7 @@ determined by this file. ``recent_project``, , global, yes, The project that will be opened on launch ``reopen_on_launch``, 1, global, yes, Whether the most recent project should be opened on launch - ``recent_map``, , global, yes, The map that will be opened on launch + ``recent_map``, , user, yes, The map that will be opened on launch ``pretty_cursors``, 1, global, yes, Whether to use custom crosshair cursors ``map_sort_order``, group, global, yes, The order map list is sorted in ``window_geometry``, , global, no, For restoring window sizes @@ -35,7 +39,7 @@ determined by this file. ``text_editor_goto_line``, , global, yes, The command that will be executed when clicking the button next the ``Script`` combo-box. ``text_editor_open_directory``, , global, yes, The command that will be executed when clicking ``Open Project in Text Editor``. ``base_game_version``, , project, no, The base pret repo for this project - ``use_encounter_json``, 1, project, yes, Enables wild encounter table editing + ``use_encounter_json``, 1, user, yes, Enables wild encounter table editing ``use_poryscript``, 0, project, yes, Whether to open .pory files for scripts ``use_custom_border_size``, 0, project, yes, Whether to allow variable border sizes ``enable_event_weather_trigger``, 1 if not ``pokefirered``, project, yes, Allows adding Weather Trigger events @@ -47,6 +51,6 @@ determined by this file. ``enable_floor_number``, 1 if ``pokefirered``, project, yes, Adds ``Floor Number`` to map headers ``create_map_text_file``, 1 if not ``pokeemerald``, project, yes, A ``text.inc`` or ``text.pory`` file will be created for any new map ``enable_triple_layer_metatiles``, 0, project, yes, Enables triple-layer metatiles (See https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles) - ``custom_scripts``, , project, yes, A list of script files to load into the scripting engine + ``custom_scripts``, , user, yes, A list of script files to load into the scripting engine Some of these settings can be toggled manually in porymap via the *Options* menu. diff --git a/include/config.h b/include/config.h index 165bab60..9746ccf6 100644 --- a/include/config.h +++ b/include/config.h @@ -9,6 +9,8 @@ #include #include +#define CONFIG_BACKWARDS_COMPATABILITY + enum MapSortOrder { Group = 0, Area = 1, @@ -145,8 +147,6 @@ public: } virtual void reset() override { this->baseGameVersion = BaseGameVersion::pokeemerald; - this->recentMap = QString(); - this->useEncounterJson = true; this->useCustomBorderSize = false; this->enableEventWeatherTrigger = true; this->enableEventSecretBase = true; @@ -157,16 +157,11 @@ public: this->enableFloorNumber = false; this->createMapTextFile = false; this->enableTripleLayerMetatiles = false; - this->customScripts.clear(); this->readKeys.clear(); } void setBaseGameVersion(BaseGameVersion baseGameVersion); BaseGameVersion getBaseGameVersion(); QString getBaseGameVersionString(); - void setRecentMap(const QString &map); - QString getRecentMap(); - void setEncounterJsonActive(bool active); - bool getEncounterJsonActive(); void setUsePoryScript(bool usePoryScript); bool getUsePoryScript(); void setProjectDir(QString projectDir); @@ -191,8 +186,6 @@ public: bool getCreateMapTextFileEnabled(); void setTripleLayerMetatilesEnabled(bool enable); bool getTripleLayerMetatilesEnabled(); - void setCustomScripts(QList scripts); - QList getCustomScripts(); protected: virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; @@ -202,8 +195,6 @@ protected: private: BaseGameVersion baseGameVersion; QString projectDir; - QString recentMap; - bool useEncounterJson; bool usePoryScript; bool useCustomBorderSize; bool enableEventWeatherTrigger; @@ -215,12 +206,50 @@ private: bool enableFloorNumber; bool createMapTextFile; bool enableTripleLayerMetatiles; - QList customScripts; QStringList readKeys; }; extern ProjectConfig projectConfig; +class UserConfig: public KeyValueConfigBase +{ +public: + UserConfig() { + reset(); + } + virtual void reset() override { + this->recentMap = QString(); + this->useEncounterJson = true; + this->customScripts.clear(); + this->readKeys.clear(); + } + void setRecentMap(const QString &map); + QString getRecentMap(); + void setEncounterJsonActive(bool active); + bool getEncounterJsonActive(); + void setProjectDir(QString projectDir); + QString getProjectDir(); + void setCustomScripts(QList scripts); + QList getCustomScripts(); +protected: + virtual QString getConfigFilepath() override; + virtual void parseConfigKeyValue(QString key, QString value) override; + virtual QMap getKeyValueMap() override; + virtual void onNewConfigFileCreated() override; + virtual void setUnreadKeys() override; +#ifdef CONFIG_BACKWARDS_COMPATABILITY + friend class ProjectConfig; +#endif +private: + QString projectDir; + QString recentMap; + bool useEncounterJson; + QList customScripts; + QStringList readKeys; +}; + +extern UserConfig userConfig; + class QAction; class Shortcut; diff --git a/src/config.cpp b/src/config.cpp index 0ed1e081..4f9d80ad 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -468,10 +468,6 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { this->baseGameVersion = BaseGameVersion::pokeemerald; logWarn(QString("Invalid config value for base_game_version: '%1'. Must be 'pokeruby', 'pokefirered' or 'pokeemerald'.").arg(value)); } - } else if (key == "recent_map") { - this->recentMap = value; - } else if (key == "use_encounter_json") { - setConfigBool(key, &this->useEncounterJson, value); } else if (key == "use_poryscript") { setConfigBool(key, &this->usePoryScript, value); } else if (key == "use_custom_border_size") { @@ -494,15 +490,21 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { setConfigBool(key, &this->createMapTextFile, value); } else if (key == "enable_triple_layer_metatiles") { setConfigBool(key, &this->enableTripleLayerMetatiles, value); +#ifdef CONFIG_BACKWARDS_COMPATABILITY + } else if (key == "recent_map") { + userConfig.setRecentMap(value); + } else if (key == "use_encounter_json") { + userConfig.setConfigBool(key, &userConfig.useEncounterJson, value); } else if (key == "custom_scripts") { - this->customScripts.clear(); + userConfig.customScripts.clear(); QList paths = value.split(","); paths.removeDuplicates(); for (QString script : paths) { if (!script.isEmpty()) { - this->customScripts.append(script); + userConfig.customScripts.append(script); } } +#endif } else { logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); } @@ -526,8 +528,6 @@ void ProjectConfig::setUnreadKeys() { QMap ProjectConfig::getKeyValueMap() { QMap map; map.insert("base_game_version", baseGameVersionMap.value(this->baseGameVersion)); - map.insert("recent_map", this->recentMap); - map.insert("use_encounter_json", QString::number(this->useEncounterJson)); map.insert("use_poryscript", QString::number(this->usePoryScript)); map.insert("use_custom_border_size", QString::number(this->useCustomBorderSize)); map.insert("enable_event_weather_trigger", QString::number(this->enableEventWeatherTrigger)); @@ -539,7 +539,6 @@ QMap ProjectConfig::getKeyValueMap() { map.insert("enable_floor_number", QString::number(this->enableFloorNumber)); map.insert("create_map_text_file", QString::number(this->createMapTextFile)); map.insert("enable_triple_layer_metatiles", QString::number(this->enableTripleLayerMetatiles)); - map.insert("custom_scripts", this->customScripts.join(",")); return map; } @@ -579,10 +578,8 @@ void ProjectConfig::onNewConfigFileCreated() { this->enableEventCloneObject = isPokefirered; this->enableFloorNumber = isPokefirered; this->createMapTextFile = (this->baseGameVersion != BaseGameVersion::pokeemerald); - this->useEncounterJson = true; this->usePoryScript = false; this->enableTripleLayerMetatiles = false; - this->customScripts.clear(); } void ProjectConfig::setProjectDir(QString projectDir) { @@ -606,24 +603,6 @@ QString ProjectConfig::getBaseGameVersionString() { return baseGameVersionMap.value(this->baseGameVersion); } -void ProjectConfig::setRecentMap(const QString &map) { - this->recentMap = map; - this->save(); -} - -QString ProjectConfig::getRecentMap() { - return this->recentMap; -} - -void ProjectConfig::setEncounterJsonActive(bool active) { - this->useEncounterJson = active; - this->save(); -} - -bool ProjectConfig::getEncounterJsonActive() { - return this->useEncounterJson; -} - void ProjectConfig::setUsePoryScript(bool usePoryScript) { this->usePoryScript = usePoryScript; this->save(); @@ -723,12 +702,82 @@ bool ProjectConfig::getTripleLayerMetatilesEnabled() { return this->enableTripleLayerMetatiles; } -void ProjectConfig::setCustomScripts(QList scripts) { +UserConfig userConfig; + +QString UserConfig::getConfigFilepath() { + // porymap config file is in the same directory as porymap itself. + return QDir(this->projectDir).filePath("porymap.user.cfg"); +} + +void UserConfig::parseConfigKeyValue(QString key, QString value) { + if (key == "recent_map") { + this->recentMap = value; + } else if (key == "use_encounter_json") { + setConfigBool(key, &this->useEncounterJson, value); + } else if (key == "custom_scripts") { + this->customScripts.clear(); + QList paths = value.split(","); + paths.removeDuplicates(); + for (QString script : paths) { + if (!script.isEmpty()) { + this->customScripts.append(script); + } + } + } else { + logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); + } + readKeys.append(key); +} + +void UserConfig::setUnreadKeys() { +} + +QMap UserConfig::getKeyValueMap() { + QMap map; + map.insert("recent_map", this->recentMap); + map.insert("use_encounter_json", QString::number(this->useEncounterJson)); + map.insert("custom_scripts", this->customScripts.join(",")); + return map; +} + +void UserConfig::onNewConfigFileCreated() { + QString dirName = QDir(this->projectDir).dirName().toLower(); + this->useEncounterJson = true; + this->customScripts.clear(); +} + +void UserConfig::setProjectDir(QString projectDir) { + this->projectDir = projectDir; +} + +QString UserConfig::getProjectDir() { + return this->projectDir; +} + +void UserConfig::setRecentMap(const QString &map) { + this->recentMap = map; + this->save(); +} + +QString UserConfig::getRecentMap() { + return this->recentMap; +} + +void UserConfig::setEncounterJsonActive(bool active) { + this->useEncounterJson = active; + this->save(); +} + +bool UserConfig::getEncounterJsonActive() { + return this->useEncounterJson; +} + +void UserConfig::setCustomScripts(QList scripts) { this->customScripts = scripts; this->save(); } -QList ProjectConfig::getCustomScripts() { +QList UserConfig::getCustomScripts() { return this->customScripts; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 71089327..9986a5bc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -357,7 +357,7 @@ void MainWindow::setWildEncountersUIEnabled(bool enabled) { void MainWindow::setProjectSpecificUIVisibility() { ui->actionUse_Poryscript->setChecked(projectConfig.getUsePoryScript()); - this->setWildEncountersUIEnabled(projectConfig.getEncounterJsonActive()); + this->setWildEncountersUIEnabled(userConfig.getEncounterJsonActive()); switch (projectConfig.getBaseGameVersion()) { @@ -509,6 +509,8 @@ bool MainWindow::openProject(QString dir) { this->statusBar()->showMessage(QString("Opening project %1").arg(nativeDir)); bool success = true; + userConfig.setProjectDir(dir); + userConfig.load(); projectConfig.setProjectDir(dir); projectConfig.load(); @@ -570,7 +572,7 @@ QString MainWindow::getDefaultMap() { if (editor && editor->project) { QList names = editor->project->groupedMapNames; if (!names.isEmpty()) { - QString recentMap = projectConfig.getRecentMap(); + QString recentMap = userConfig.getRecentMap(); if (!recentMap.isNull() && recentMap.length() > 0) { for (int i = 0; i < names.length(); i++) { if (names.value(i).contains(recentMap)) { @@ -597,8 +599,8 @@ QString MainWindow::getExistingDirectory(QString dir) { void MainWindow::on_action_Open_Project_triggered() { QString recent = "."; - if (!projectConfig.getRecentMap().isEmpty()) { - recent = projectConfig.getRecentMap(); + if (!userConfig.getRecentMap().isEmpty()) { + recent = userConfig.getRecentMap(); } QString dir = getExistingDirectory(recent); if (!dir.isEmpty()) { @@ -742,7 +744,7 @@ void MainWindow::openWarpMap(QString map_name, QString event_id, QString event_g } void MainWindow::setRecentMap(QString mapName) { - projectConfig.setRecentMap(mapName); + userConfig.setRecentMap(mapName); } void MainWindow::displayMapProperties() { @@ -1707,7 +1709,7 @@ void MainWindow::on_mainTabBar_tabBarClicked(int index) editor->setEditingConnections(); } if (index != 4) { - if (projectConfig.getEncounterJsonActive()) + if (userConfig.getEncounterJsonActive()) editor->saveEncounterTabData(); } if (index != 1) { @@ -1758,7 +1760,7 @@ void MainWindow::on_actionUse_Encounter_Json_triggered(bool checked) warning.setText("You must reload the project for this setting to take effect."); warning.setIcon(QMessageBox::Information); warning.exec(); - projectConfig.setEncounterJsonActive(checked); + userConfig.setEncounterJsonActive(checked); } void MainWindow::on_actionMonitor_Project_Files_triggered(bool checked) @@ -3217,6 +3219,7 @@ void MainWindow::closeEvent(QCloseEvent *event) { } } projectConfig.save(); + userConfig.save(); } porymapConfig.setMainGeometry( diff --git a/src/mainwindow_scriptapi.cpp b/src/mainwindow_scriptapi.cpp index 5875bb08..75d6b417 100644 --- a/src/mainwindow_scriptapi.cpp +++ b/src/mainwindow_scriptapi.cpp @@ -1052,7 +1052,7 @@ QString MainWindow::getBaseGameVersion() { } QList MainWindow::getCustomScripts() { - return projectConfig.getCustomScripts(); + return userConfig.getCustomScripts(); } int MainWindow::getMainTab() { @@ -1065,7 +1065,7 @@ void MainWindow::setMainTab(int index) { if (!this->ui || !this->ui->mainTabBar || index < 0 || index >= this->ui->mainTabBar->count()) return; // Can't select Wild Encounters tab if it's disabled - if (index == 4 && !projectConfig.getEncounterJsonActive()) + if (index == 4 && !userConfig.getEncounterJsonActive()) return; this->on_mainTabBar_tabBarClicked(index); } diff --git a/src/project.cpp b/src/project.cpp index beda1e79..706cc306 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -727,7 +727,7 @@ void Project::saveMapGroups() { } void Project::saveWildMonData() { - if (!projectConfig.getEncounterJsonActive()) return; + if (!userConfig.getEncounterJsonActive()) return; QString wildEncountersJsonFilepath = QString("%1/src/data/wild_encounters.json").arg(root); QFile wildEncountersFile(wildEncountersJsonFilepath); @@ -1716,7 +1716,7 @@ bool Project::readWildMonData() { wildMonFields.clear(); wildMonData.clear(); encounterGroupLabels.clear(); - if (!projectConfig.getEncounterJsonActive()) { + if (!userConfig.getEncounterJsonActive()) { return true; } @@ -1727,7 +1727,7 @@ bool Project::readWildMonData() { if (!parser.tryParseOrderedJsonFile(&wildMonObj, wildMonJsonFilepath)) { // Failing to read wild encounters data is not a critical error, just disable the // encounter editor and log a warning in case the user intended to have this data. - projectConfig.setEncounterJsonActive(false); + userConfig.setEncounterJsonActive(false); emit disableWildEncountersUI(); logWarn(QString("Failed to read wild encounters from %1").arg(wildMonJsonFilepath)); return true; @@ -2295,7 +2295,7 @@ bool Project::readObjEventGfxConstants() { bool Project::readMiscellaneousConstants() { miscConstants.clear(); - if (projectConfig.getEncounterJsonActive()) { + if (userConfig.getEncounterJsonActive()) { QString filename = "include/constants/pokemon.h"; fileWatcher.addPath(root + "/" + filename); QMap pokemonDefines = parser.readCDefines(filename, { "MIN_", "MAX_" }); diff --git a/src/scripting.cpp b/src/scripting.cpp index ef757464..58f64b1c 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -30,7 +30,7 @@ Scripting::Scripting(MainWindow *mainWindow) { this->engine = new QJSEngine(mainWindow); this->engine->installExtensions(QJSEngine::ConsoleExtension); this->engine->globalObject().setProperty("map", this->engine->newQObject(mainWindow)); - for (QString script : projectConfig.getCustomScripts()) { + for (QString script : userConfig.getCustomScripts()) { this->filepaths.append(script); } this->loadModules(this->filepaths); @@ -40,7 +40,7 @@ void Scripting::loadModules(QStringList moduleFiles) { for (QString filepath : moduleFiles) { QJSValue module = this->engine->importModule(filepath); if (module.isError()) { - QString relativePath = QDir::cleanPath(projectConfig.getProjectDir() + QDir::separator() + filepath); + QString relativePath = QDir::cleanPath(userConfig.getProjectDir() + QDir::separator() + filepath); module = this->engine->importModule(relativePath); if (tryErrorJS(module)) continue; }