diff --git a/CHANGELOG.md b/CHANGELOG.md index db5cc57e33..5b2a05f749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Bug #7088: Deleting last save game of last character doesn't clear character name/details Feature #5492: Let rain and snow collide with statics Feature #6447: Add LOD support to Object Paging + Feature #6922: Improve launcher appearance Feature #6933: Support high-resolution cursor textures Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData Feature #6979: Add support of loading and displaying LOD assets purely based on their filename extension diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ee7f3a131..787d525cb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -820,12 +820,20 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE) get_filename_component(QT_COCOA_PLUGIN_GROUP "${QT_COCOA_PLUGIN_DIR}" NAME) get_filename_component(QT_COCOA_PLUGIN_NAME "${QT_COCOA_PLUGIN_PATH}" NAME) configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY) + + get_property(QT_QMACSTYLE_PLUGIN_PATH TARGET Qt5::QMacStylePlugin PROPERTY LOCATION_RELEASE) + get_filename_component(QT_QMACSTYLE_PLUGIN_DIR "${QT_QMACSTYLE_PLUGIN_PATH}" DIRECTORY) + get_filename_component(QT_QMACSTYLE_PLUGIN_GROUP "${QT_QMACSTYLE_PLUGIN_DIR}" NAME) + get_filename_component(QT_QMACSTYLE_PLUGIN_NAME "${QT_QMACSTYLE_PLUGIN_PATH}" NAME) + configure_file("${QT_QMACSTYLE_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}" COPYONLY) + configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${APP_BUNDLE_DIR}/Contents/Resources/qt.conf" COPYONLY) if (BUILD_OPENCS) get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME) set(OPENCS_BUNDLE_NAME "${OPENCS_BUNDLE_NAME_TMP}.app") configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY) + configure_file("${QT_QMACSTYLE_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}" COPYONLY) configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${OPENCS_BUNDLE_NAME}/Contents/Resources/qt.conf" COPYONLY) endif () @@ -895,7 +903,9 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE) install_plugins_for_bundle("${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") + set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}") set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}") + set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/PlugIns/${QT_QMACSTYLE_PLUGIN_GROUP}/${QT_QMACSTYLE_PLUGIN_NAME}") install(CODE " function(gp_item_default_embedded_path_override item default_embedded_path_var) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 79a27094f2..1fb69060bc 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -4,10 +4,9 @@ set(LAUNCHER sdlinit.cpp main.cpp maindialog.cpp - playpage.cpp textslotmsgbox.cpp + importpage.cpp settingspage.cpp - advancedpage.cpp utils/cellnameloader.cpp utils/profilescombobox.cpp @@ -23,10 +22,9 @@ set(LAUNCHER_HEADER graphicspage.hpp sdlinit.hpp maindialog.hpp - playpage.hpp textslotmsgbox.hpp + importpage.hpp settingspage.hpp - advancedpage.hpp utils/cellnameloader.hpp utils/profilescombobox.hpp @@ -40,10 +38,9 @@ set(LAUNCHER_UI ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui ${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui - ${CMAKE_SOURCE_DIR}/files/ui/playpage.ui ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui + ${CMAKE_SOURCE_DIR}/files/ui/importpage.ui ${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui - ${CMAKE_SOURCE_DIR}/files/ui/advancedpage.ui ${CMAKE_SOURCE_DIR}/files/ui/directorypicker.ui ) diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp deleted file mode 100644 index 9fe478f94a..0000000000 --- a/apps/launcher/advancedpage.cpp +++ /dev/null @@ -1,490 +0,0 @@ -#include "advancedpage.hpp" - -#include -#include -#include - -#include -#include -#include - -#include - -#include "utils/openalutil.hpp" - -Launcher::AdvancedPage::AdvancedPage(Config::GameSettings& gameSettings, QWidget* parent) - : QWidget(parent) - , mGameSettings(gameSettings) -{ - setObjectName("AdvancedPage"); - setupUi(this); - - for (const std::string& name : Launcher::enumerateOpenALDevices()) - { - audioDeviceSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name)); - } - for (const std::string& name : Launcher::enumerateOpenALDevicesHrtf()) - { - hrtfProfileSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name)); - } - - loadSettings(); - - mCellNameCompleter.setModel(&mCellNameCompleterModel); - startDefaultCharacterAtField->setCompleter(&mCellNameCompleter); -} - -void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) -{ - // Update the list of suggestions for the "Start default character at" field - mCellNameCompleterModel.setStringList(cellNames); - mCellNameCompleter.setCompletionMode(QCompleter::PopupCompletion); - mCellNameCompleter.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); -} - -void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) -{ - startDefaultCharacterAtLabel->setEnabled(state == Qt::Checked); - startDefaultCharacterAtField->setEnabled(state == Qt::Checked); -} - -void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked() -{ - QString scriptFile = QFileDialog::getOpenFileName( - this, QObject::tr("Select script file"), QDir::currentPath(), QString(tr("Text file (*.txt)"))); - - if (scriptFile.isEmpty()) - return; - - QFileInfo info(scriptFile); - - if (!info.exists() || !info.isReadable()) - return; - - const QString path(QDir::toNativeSeparators(info.absoluteFilePath())); - runScriptAfterStartupField->setText(path); -} - -namespace -{ - constexpr double CellSizeInUnits = 8192; - - double convertToCells(double unitRadius) - { - return unitRadius / CellSizeInUnits; - } - - int convertToUnits(double CellGridRadius) - { - return static_cast(CellSizeInUnits * CellGridRadius); - } -} - -bool Launcher::AdvancedPage::loadSettings() -{ - // Game mechanics - { - loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); - loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); - loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); - loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); - loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); - loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); - loadSettingBool(classicCalmSpellsCheckBox, "classic calm spells behavior", "Game"); - loadSettingBool( - requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game"); - loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); - loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); - loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); - loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); - int unarmedFactorsStrengthIndex = Settings::Manager::getInt("strength influences hand to hand", "Game"); - if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) - unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); - loadSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game"); - loadSettingBool(enableNavigatorCheckBox, "enable", "Navigator"); - int numPhysicsThreads = Settings::Manager::getInt("async num threads", "Physics"); - if (numPhysicsThreads >= 0) - physicsThreadsSpinBox->setValue(numPhysicsThreads); - loadSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game"); - loadSettingBool(unarmedCreatureAttacksDamageArmorCheckBox, "unarmed creature attacks damage armor", "Game"); - const int actorCollisionShapeType = Settings::Manager::getInt("actor collision shape type", "Game"); - if (0 <= actorCollisionShapeType && actorCollisionShapeType < actorCollisonShapeTypeComboBox->count()) - actorCollisonShapeTypeComboBox->setCurrentIndex(actorCollisionShapeType); - } - - // Visuals - { - loadSettingBool(autoUseObjectNormalMapsCheckBox, "auto use object normal maps", "Shaders"); - loadSettingBool(autoUseObjectSpecularMapsCheckBox, "auto use object specular maps", "Shaders"); - loadSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders"); - loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders"); - loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders"); - loadSettingBool(softParticlesCheckBox, "soft particles", "Shaders"); - loadSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders"); - if (Settings::Manager::getInt("antialiasing", "Video") == 0) - { - antialiasAlphaTestCheckBox->setCheckState(Qt::Unchecked); - } - loadSettingBool(adjustCoverageForAlphaTestCheckBox, "adjust coverage for alpha test", "Shaders"); - loadSettingBool(weatherParticleOcclusionCheckBox, "weather particle occlusion", "Shaders"); - loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game"); - connect(animSourcesCheckBox, &QCheckBox::toggled, this, &AdvancedPage::slotAnimSourcesToggled); - loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); - if (animSourcesCheckBox->checkState() != Qt::Unchecked) - { - loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); - loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); - } - loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); - loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game"); - - const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); - const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain"); - if (distantTerrain && objectPaging) - { - distantLandCheckBox->setCheckState(Qt::Checked); - } - - loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); - viewingDistanceComboBox->setValue(convertToCells(Settings::Manager::getInt("viewing distance", "Camera"))); - objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble("object paging min size", "Terrain")); - - loadSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game"); - - connect(postprocessEnabledCheckBox, &QCheckBox::toggled, this, &AdvancedPage::slotPostProcessToggled); - loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing"); - loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing"); - postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("auto exposure speed", "Post Processing")); - - connect(skyBlendingCheckBox, &QCheckBox::toggled, this, &AdvancedPage::slotSkyBlendingToggled); - loadSettingBool(radialFogCheckBox, "radial fog", "Fog"); - loadSettingBool(exponentialFogCheckBox, "exponential fog", "Fog"); - loadSettingBool(skyBlendingCheckBox, "sky blending", "Fog"); - skyBlendingStartComboBox->setValue(Settings::Manager::getDouble("sky blending start", "Fog")); - } - - // Audio - { - const std::string& selectedAudioDevice = Settings::Manager::getString("device", "Sound"); - if (selectedAudioDevice.empty() == false) - { - int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice)); - if (audioDeviceIndex != -1) - { - audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex); - } - } - int hrtfEnabledIndex = Settings::Manager::getInt("hrtf enable", "Sound"); - if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1) - { - enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1); - } - const std::string& selectedHRTFProfile = Settings::Manager::getString("hrtf", "Sound"); - if (selectedHRTFProfile.empty() == false) - { - int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile)); - if (hrtfProfileIndex != -1) - { - hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex); - } - } - } - - // Interface Changes - { - loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game"); - loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game"); - loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); - loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); - loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); - int showOwnedIndex = Settings::Manager::getInt("show owned", "Game"); - // Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid. - if (showOwnedIndex >= 0 && showOwnedIndex <= 3) - showOwnedComboBox->setCurrentIndex(showOwnedIndex); - loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); - loadSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map"); - loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); - scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI")); - fontSizeSpinBox->setValue(Settings::Manager::getInt("font size", "GUI")); - } - - // Bug fixes - { - loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); - loadSettingBool( - trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); - } - - // Miscellaneous - { - // Saves - loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); - loadSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves"); - - // Other Settings - QString screenshotFormatString - = QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper(); - if (screenshotFormatComboBox->findText(screenshotFormatString) == -1) - screenshotFormatComboBox->addItem(screenshotFormatString); - screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString)); - - loadSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General"); - } - - // Testing - { - loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); - - bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1; - if (skipMenu) - { - skipMenuCheckBox->setCheckState(Qt::Checked); - } - startDefaultCharacterAtLabel->setEnabled(skipMenu); - startDefaultCharacterAtField->setEnabled(skipMenu); - - startDefaultCharacterAtField->setText(mGameSettings.value("start")); - runScriptAfterStartupField->setText(mGameSettings.value("script-run")); - } - return true; -} - -void Launcher::AdvancedPage::saveSettings() -{ - // Game mechanics - { - saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); - saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); - saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); - saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); - saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); - saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); - saveSettingBool(classicCalmSpellsCheckBox, "classic calm spells behavior", "Game"); - saveSettingBool( - requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game"); - saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); - saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); - saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); - saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); - saveSettingInt(unarmedFactorsStrengthComboBox, "strength influences hand to hand", "Game"); - saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game"); - saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator"); - saveSettingInt(physicsThreadsSpinBox, "async num threads", "Physics"); - saveSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game"); - saveSettingBool(unarmedCreatureAttacksDamageArmorCheckBox, "unarmed creature attacks damage armor", "Game"); - saveSettingInt(actorCollisonShapeTypeComboBox, "actor collision shape type", "Game"); - } - - // Visuals - { - saveSettingBool(autoUseObjectNormalMapsCheckBox, "auto use object normal maps", "Shaders"); - saveSettingBool(autoUseObjectSpecularMapsCheckBox, "auto use object specular maps", "Shaders"); - saveSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders"); - saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders"); - saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders"); - saveSettingBool(radialFogCheckBox, "radial fog", "Fog"); - saveSettingBool(softParticlesCheckBox, "soft particles", "Shaders"); - saveSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders"); - saveSettingBool(adjustCoverageForAlphaTestCheckBox, "adjust coverage for alpha test", "Shaders"); - saveSettingBool(weatherParticleOcclusionCheckBox, "weather particle occlusion", "Shaders"); - saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game"); - saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); - saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); - saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); - saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); - saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game"); - - const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); - const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain"); - const bool wantDistantLand = distantLandCheckBox->checkState(); - if (wantDistantLand != (distantTerrain && objectPaging)) - { - Settings::Manager::setBool("distant terrain", "Terrain", wantDistantLand); - Settings::Manager::setBool("object paging", "Terrain", wantDistantLand); - } - - saveSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); - int viewingDistance = convertToUnits(viewingDistanceComboBox->value()); - if (viewingDistance != Settings::Manager::getInt("viewing distance", "Camera")) - { - Settings::Manager::setInt("viewing distance", "Camera", viewingDistance); - } - double objectPagingMinSize = objectPagingMinSizeComboBox->value(); - if (objectPagingMinSize != Settings::Manager::getDouble("object paging min size", "Terrain")) - Settings::Manager::setDouble("object paging min size", "Terrain", objectPagingMinSize); - - saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game"); - - saveSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing"); - saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing"); - double hdrExposureTime = postprocessHDRTimeComboBox->value(); - if (hdrExposureTime != Settings::Manager::getDouble("auto exposure speed", "Post Processing")) - Settings::Manager::setDouble("auto exposure speed", "Post Processing", hdrExposureTime); - - saveSettingBool(radialFogCheckBox, "radial fog", "Fog"); - saveSettingBool(exponentialFogCheckBox, "exponential fog", "Fog"); - saveSettingBool(skyBlendingCheckBox, "sky blending", "Fog"); - Settings::Manager::setDouble("sky blending start", "Fog", skyBlendingStartComboBox->value()); - } - - // Audio - { - int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex(); - const std::string& prevAudioDevice = Settings::Manager::getString("device", "Sound"); - if (audioDeviceIndex != 0) - { - const std::string& newAudioDevice = audioDeviceSelectorComboBox->currentText().toUtf8().constData(); - if (newAudioDevice != prevAudioDevice) - Settings::Manager::setString("device", "Sound", newAudioDevice); - } - else if (!prevAudioDevice.empty()) - { - Settings::Manager::setString("device", "Sound", {}); - } - int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1; - if (hrtfEnabledIndex != Settings::Manager::getInt("hrtf enable", "Sound")) - { - Settings::Manager::setInt("hrtf enable", "Sound", hrtfEnabledIndex); - } - int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex(); - const std::string& prevHRTFProfile = Settings::Manager::getString("hrtf", "Sound"); - if (selectedHRTFProfileIndex != 0) - { - const std::string& newHRTFProfile = hrtfProfileSelectorComboBox->currentText().toUtf8().constData(); - if (newHRTFProfile != prevHRTFProfile) - Settings::Manager::setString("hrtf", "Sound", newHRTFProfile); - } - else if (!prevHRTFProfile.empty()) - { - Settings::Manager::setString("hrtf", "Sound", {}); - } - } - - // Interface Changes - { - saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game"); - saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game"); - saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); - saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); - saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); - saveSettingInt(showOwnedComboBox, "show owned", "Game"); - saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); - saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map"); - saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); - - float uiScalingFactor = scalingSpinBox->value(); - if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI")) - Settings::Manager::setFloat("scaling factor", "GUI", uiScalingFactor); - - int fontSize = fontSizeSpinBox->value(); - if (fontSize != Settings::Manager::getInt("font size", "GUI")) - Settings::Manager::setInt("font size", "GUI", fontSize); - } - - // Bug fixes - { - saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); - saveSettingBool( - trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); - } - - // Miscellaneous - { - // Saves Settings - saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); - saveSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves"); - - // Other Settings - std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString(); - if (screenshotFormatString != Settings::Manager::getString("screenshot format", "General")) - Settings::Manager::setString("screenshot format", "General", screenshotFormatString); - - saveSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General"); - } - - // Testing - { - saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); - - int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked; - if (skipMenu != mGameSettings.value("skip-menu").toInt()) - mGameSettings.setValue("skip-menu", QString::number(skipMenu)); - - QString startCell = startDefaultCharacterAtField->text(); - if (startCell != mGameSettings.value("start")) - { - mGameSettings.setValue("start", startCell); - } - QString scriptRun = runScriptAfterStartupField->text(); - if (scriptRun != mGameSettings.value("script-run")) - mGameSettings.setValue("script-run", scriptRun); - } -} - -void Launcher::AdvancedPage::loadSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group) -{ - if (Settings::Manager::getBool(setting, group)) - checkbox->setCheckState(Qt::Checked); -} - -void Launcher::AdvancedPage::saveSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group) -{ - bool cValue = checkbox->checkState(); - if (cValue != Settings::Manager::getBool(setting, group)) - Settings::Manager::setBool(setting, group, cValue); -} - -void Launcher::AdvancedPage::loadSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group) -{ - int currentIndex = Settings::Manager::getInt(setting, group); - comboBox->setCurrentIndex(currentIndex); -} - -void Launcher::AdvancedPage::saveSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group) -{ - int currentIndex = comboBox->currentIndex(); - if (currentIndex != Settings::Manager::getInt(setting, group)) - Settings::Manager::setInt(setting, group, currentIndex); -} - -void Launcher::AdvancedPage::loadSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group) -{ - int value = Settings::Manager::getInt(setting, group); - spinBox->setValue(value); -} - -void Launcher::AdvancedPage::saveSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group) -{ - int value = spinBox->value(); - if (value != Settings::Manager::getInt(setting, group)) - Settings::Manager::setInt(setting, group, value); -} - -void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames) -{ - loadCellsForAutocomplete(cellNames); -} - -void Launcher::AdvancedPage::slotAnimSourcesToggled(bool checked) -{ - weaponSheathingCheckBox->setEnabled(checked); - shieldSheathingCheckBox->setEnabled(checked); - if (!checked) - { - weaponSheathingCheckBox->setCheckState(Qt::Unchecked); - shieldSheathingCheckBox->setCheckState(Qt::Unchecked); - } -} - -void Launcher::AdvancedPage::slotPostProcessToggled(bool checked) -{ - postprocessTransparentPostpassCheckBox->setEnabled(checked); - postprocessHDRTimeComboBox->setEnabled(checked); - postprocessHDRTimeLabel->setEnabled(checked); -} - -void Launcher::AdvancedPage::slotSkyBlendingToggled(bool checked) -{ - skyBlendingStartComboBox->setEnabled(checked); - skyBlendingStartLabel->setEnabled(checked); -} diff --git a/apps/launcher/advancedpage.hpp b/apps/launcher/advancedpage.hpp deleted file mode 100644 index 0d5bc258d3..0000000000 --- a/apps/launcher/advancedpage.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef ADVANCEDPAGE_H -#define ADVANCEDPAGE_H - -#include -#include - -#include "ui_advancedpage.h" - -#include - -namespace Config -{ - class GameSettings; -} - -namespace Launcher -{ - class AdvancedPage : public QWidget, private Ui::AdvancedPage - { - Q_OBJECT - - public: - explicit AdvancedPage(Config::GameSettings& gameSettings, QWidget* parent = nullptr); - - bool loadSettings(); - void saveSettings(); - - public slots: - void slotLoadedCellsChanged(QStringList cellNames); - - private slots: - void on_skipMenuCheckBox_stateChanged(int state); - void on_runScriptAfterStartupBrowseButton_clicked(); - void slotAnimSourcesToggled(bool checked); - void slotPostProcessToggled(bool checked); - void slotSkyBlendingToggled(bool checked); - - private: - Config::GameSettings& mGameSettings; - QCompleter mCellNameCompleter; - QStringListModel mCellNameCompleterModel; - - /** - * Load the cells associated with the given content files for use in autocomplete - * @param filePaths the file paths of the content files to be examined - */ - void loadCellsForAutocomplete(QStringList filePaths); - static void loadSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group); - static void saveSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group); - static void loadSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group); - static void saveSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group); - static void loadSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group); - static void saveSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group); - }; -} -#endif diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 7a1a863978..eb8258c09d 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -112,6 +112,12 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager& cfg, Config: const QString encoding = mGameSettings.value("encoding", "win1252"); mSelector->setEncoding(encoding); + QStringList languages; + languages << tr("English") << tr("French") << tr("German") << tr("Italian") << tr("Polish") << tr("Russian") + << tr("Spanish"); + + mSelector->languageBox()->addItems(languages); + mNewProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); mCloneProfileDialog = new TextInputDialog(tr("Clone Content List"), tr("Content List name:"), this); @@ -196,6 +202,11 @@ bool Launcher::DataFilesPage::loadSettings() if (!currentProfile.isEmpty()) addProfile(currentProfile, true); + QString language(mLauncherSettings.value(QLatin1String("Settings/language"))); + int index = mSelector->languageBox()->findText(language); + if (index != -1) + mSelector->languageBox()->setCurrentIndex(index); + return true; } @@ -340,6 +351,23 @@ void Launcher::DataFilesPage::saveSettings(const QString& profile) } mLauncherSettings.setContentList(profileName, dirList, selectedArchivePaths(), fileNames); mGameSettings.setContentList(dirList, selectedArchivePaths(), fileNames); + + QString language(mSelector->languageBox()->currentText()); + + mLauncherSettings.setValue(QLatin1String("Settings/language"), language); + + if (language == QLatin1String("Polish")) + { + mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250")); + } + else if (language == QLatin1String("Russian")) + { + mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251")); + } + else + { + mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252")); + } } QStringList Launcher::DataFilesPage::selectedDirectoriesPaths() const diff --git a/apps/launcher/importpage.cpp b/apps/launcher/importpage.cpp new file mode 100644 index 0000000000..f7230043f3 --- /dev/null +++ b/apps/launcher/importpage.cpp @@ -0,0 +1,229 @@ +#include "importpage.hpp" + +#include +#include +#include +#include + +#include + +#include "utils/textinputdialog.hpp" + +using namespace Process; + +Launcher::ImportPage::ImportPage(Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, + Config::LauncherSettings& launcherSettings, MainDialog* parent) + : QWidget(parent) + , mCfgMgr(cfg) + , mGameSettings(gameSettings) + , mLauncherSettings(launcherSettings) + , mMain(parent) +{ + setupUi(this); + + mWizardInvoker = new ProcessInvoker(); + mImporterInvoker = new ProcessInvoker(); + resetProgressBar(); + + connect(mWizardInvoker->getProcess(), &QProcess::started, this, &ImportPage::wizardStarted); + + connect(mWizardInvoker->getProcess(), qOverload(&QProcess::finished), this, + &ImportPage::wizardFinished); + + connect(mImporterInvoker->getProcess(), &QProcess::started, this, &ImportPage::importerStarted); + + connect(mImporterInvoker->getProcess(), qOverload(&QProcess::finished), this, + &ImportPage::importerFinished); + + // Detect Morrowind configuration files + QStringList iniPaths; + + for (const QString& path : mGameSettings.getDataDirs()) + { + QDir dir(path); + dir.setPath(dir.canonicalPath()); // Resolve symlinks + + if (dir.exists(QString("Morrowind.ini"))) + iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); + else + { + if (!dir.cdUp()) + continue; // Cannot move from Data Files + + if (dir.exists(QString("Morrowind.ini"))) + iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); + } + } + + if (!iniPaths.isEmpty()) + { + settingsComboBox->addItems(iniPaths); + importerButton->setEnabled(true); + } + else + { + importerButton->setEnabled(false); + } + + loadSettings(); +} + +Launcher::ImportPage::~ImportPage() +{ + delete mWizardInvoker; + delete mImporterInvoker; +} + +void Launcher::ImportPage::on_wizardButton_clicked() +{ + mMain->writeSettings(); + + if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) + return; +} + +void Launcher::ImportPage::on_importerButton_clicked() +{ + mMain->writeSettings(); + + // Create the file if it doesn't already exist, else the importer will fail + auto path = mCfgMgr.getUserConfigPath(); + path /= "openmw.cfg"; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QFile file(path); +#else + QFile file(Files::pathToQString(path)); +#endif + + if (!file.exists()) + { + if (!file.open(QIODevice::ReadWrite)) + { + // File cannot be created + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText( + tr("

Could not open or create %1 for writing

\ +

Please make sure you have the right permissions \ + and try again.

") + .arg(file.fileName())); + msgBox.exec(); + return; + } + + file.close(); + } + + // Construct the arguments to run the importer + QStringList arguments; + + if (addonsCheckBox->isChecked()) + arguments.append(QString("--game-files")); + + if (fontsCheckBox->isChecked()) + arguments.append(QString("--fonts")); + + arguments.append(QString("--encoding")); + arguments.append(mGameSettings.value(QString("encoding"), QString("win1252"))); + arguments.append(QString("--ini")); + arguments.append(settingsComboBox->currentText()); + arguments.append(QString("--cfg")); + arguments.append(Files::pathToQString(path)); + + qDebug() << "arguments " << arguments; + + // start the progress bar as a "bouncing ball" + progressBar->setMaximum(0); + progressBar->setValue(0); + if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false)) + { + resetProgressBar(); + } +} + +void Launcher::ImportPage::on_browseButton_clicked() +{ + QString iniFile = QFileDialog::getOpenFileName(this, QObject::tr("Select configuration file"), QDir::currentPath(), + QString(tr("Morrowind configuration file (*.ini)"))); + + if (iniFile.isEmpty()) + return; + + QFileInfo info(iniFile); + + if (!info.exists() || !info.isReadable()) + return; + + const QString path(QDir::toNativeSeparators(info.absoluteFilePath())); + + if (settingsComboBox->findText(path) == -1) + { + settingsComboBox->addItem(path); + settingsComboBox->setCurrentIndex(settingsComboBox->findText(path)); + importerButton->setEnabled(true); + } +} + +void Launcher::ImportPage::wizardStarted() +{ + mMain->hide(); // Hide the launcher + + wizardButton->setEnabled(false); +} + +void Launcher::ImportPage::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (exitCode != 0 || exitStatus == QProcess::CrashExit) + return qApp->quit(); + + mMain->reloadSettings(); + wizardButton->setEnabled(true); + + mMain->show(); // Show the launcher again +} + +void Launcher::ImportPage::importerStarted() +{ + importerButton->setEnabled(false); +} + +void Launcher::ImportPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (exitCode != 0 || exitStatus == QProcess::CrashExit) + { + resetProgressBar(); + + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Importer finished")); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setText(tr("Failed to import settings from INI file.")); + msgBox.exec(); + } + else + { + // indicate progress finished + progressBar->setMaximum(1); + progressBar->setValue(1); + + // Importer may have changed settings, so refresh + mMain->reloadSettings(); + } + + importerButton->setEnabled(true); +} + +void Launcher::ImportPage::resetProgressBar() +{ + // set progress bar to 0 % + progressBar->reset(); +} + +void Launcher::ImportPage::saveSettings() {} + +bool Launcher::ImportPage::loadSettings() +{ + return true; +} diff --git a/apps/launcher/importpage.hpp b/apps/launcher/importpage.hpp new file mode 100644 index 0000000000..c7742c01df --- /dev/null +++ b/apps/launcher/importpage.hpp @@ -0,0 +1,64 @@ +#ifndef IMPORTSPAGE_HPP +#define IMPORTSPAGE_HPP + +#include + +#include "ui_importpage.h" + +#include "maindialog.hpp" + +namespace Files +{ + struct ConfigurationManager; +} +namespace Config +{ + class GameSettings; + class LauncherSettings; +} + +namespace Launcher +{ + class TextInputDialog; + + class ImportPage : public QWidget, private Ui::ImportPage + { + Q_OBJECT + + public: + ImportPage(Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, + Config::LauncherSettings& launcherSettings, MainDialog* parent = nullptr); + ~ImportPage() override; + + void saveSettings(); + bool loadSettings(); + + /// set progress bar on page to 0% + void resetProgressBar(); + + private slots: + + void on_wizardButton_clicked(); + void on_importerButton_clicked(); + void on_browseButton_clicked(); + + void wizardStarted(); + void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus); + + void importerStarted(); + void importerFinished(int exitCode, QProcess::ExitStatus exitStatus); + + private: + Process::ProcessInvoker* mWizardInvoker; + Process::ProcessInvoker* mImporterInvoker; + + Files::ConfigurationManager& mCfgMgr; + + Config::GameSettings& mGameSettings; + Config::LauncherSettings& mLauncherSettings; + + MainDialog* mMain; + }; +} + +#endif // IMPORTSPAGE_HPP diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 309704c87f..6d2f5593fc 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -15,10 +15,9 @@ #include #include -#include "advancedpage.hpp" #include "datafilespage.hpp" #include "graphicspage.hpp" -#include "playpage.hpp" +#include "importpage.hpp" #include "settingspage.hpp" using namespace Process; @@ -47,21 +46,13 @@ Launcher::MainDialog::MainDialog(QWidget* parent) connect(mWizardInvoker->getProcess(), qOverload(&QProcess::finished), this, &MainDialog::wizardFinished); - iconWidget->setViewMode(QListView::IconMode); - iconWidget->setWrapping(false); - iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure - iconWidget->setIconSize(QSize(48, 48)); - iconWidget->setMovement(QListView::Static); - - iconWidget->setSpacing(4); - iconWidget->setCurrentRow(0); - iconWidget->setFlow(QListView::LeftToRight); - - auto* helpButton = new QPushButton(tr("Help")); - auto* playButton = new QPushButton(tr("Play")); buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close")); - buttonBox->addButton(helpButton, QDialogButtonBox::HelpRole); - buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole); + buttonBox->button(QDialogButtonBox::Ok)->setText(tr(" Launch OpenMW ")); + buttonBox->button(QDialogButtonBox::Help)->setText(tr("Help")); + + // Order of buttons can be different on different setups, + // so make sure that the Play button has a focus by default. + buttonBox->button(QDialogButtonBox::Ok)->setFocus(); connect(buttonBox, &QDialogButtonBox::rejected, this, &MainDialog::close); connect(buttonBox, &QDialogButtonBox::accepted, this, &MainDialog::play); @@ -84,37 +75,10 @@ void Launcher::MainDialog::createIcons() if (!QIcon::hasThemeIcon("document-new")) QIcon::setThemeName("tango"); - auto* playButton = new QListWidgetItem(iconWidget); - playButton->setIcon(QIcon(":/images/openmw.png")); - playButton->setText(tr("Play")); - playButton->setTextAlignment(Qt::AlignCenter); - playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - auto* dataFilesButton = new QListWidgetItem(iconWidget); - dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png")); - dataFilesButton->setText(tr("Data Files")); - dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); - dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - auto* graphicsButton = new QListWidgetItem(iconWidget); - graphicsButton->setIcon(QIcon(":/images/preferences-video.png")); - graphicsButton->setText(tr("Graphics")); - graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute); - graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - auto* settingsButton = new QListWidgetItem(iconWidget); - settingsButton->setIcon(QIcon(":/images/preferences.png")); - settingsButton->setText(tr("Settings")); - settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); - settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - auto* advancedButton = new QListWidgetItem(iconWidget); - advancedButton->setIcon(QIcon(":/images/preferences-advanced.png")); - advancedButton->setText(tr("Advanced")); - advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom); - advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - connect(iconWidget, &QListWidget::currentItemChanged, this, &MainDialog::changePage); + connect(dataAction, &QAction::triggered, this, &MainDialog::enableDataPage); + connect(graphicsAction, &QAction::triggered, this, &MainDialog::enableGraphicsPage); + connect(settingsAction, &QAction::triggered, this, &MainDialog::enableSettingsPage); + connect(importAction, &QAction::triggered, this, &MainDialog::enableImportPage); } void Launcher::MainDialog::createPages() @@ -123,33 +87,23 @@ void Launcher::MainDialog::createPages() if (pagesWidget->count() != 0) return; - mPlayPage = new PlayPage(this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); mGraphicsPage = new GraphicsPage(this); - mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this); - mAdvancedPage = new AdvancedPage(mGameSettings, this); - - // Set the combobox of the play page to imitate the combobox on the datafilespage - mPlayPage->setProfilesModel(mDataFilesPage->profilesModel()); - mPlayPage->setProfilesIndex(mDataFilesPage->profilesIndex()); + mImportPage = new ImportPage(mCfgMgr, mGameSettings, mLauncherSettings, this); + mSettingsPage = new SettingsPage(mGameSettings, this); // Add the pages to the stacked widget - pagesWidget->addWidget(mPlayPage); pagesWidget->addWidget(mDataFilesPage); pagesWidget->addWidget(mGraphicsPage); pagesWidget->addWidget(mSettingsPage); - pagesWidget->addWidget(mAdvancedPage); + pagesWidget->addWidget(mImportPage); // Select the first page - iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select); + dataAction->setChecked(true); - connect(mPlayPage, &PlayPage::playButtonClicked, this, &MainDialog::play); - - connect(mPlayPage, &PlayPage::signalProfileChanged, mDataFilesPage, &DataFilesPage::slotProfileChanged); - connect(mDataFilesPage, &DataFilesPage::signalProfileChanged, mPlayPage, &PlayPage::setProfilesIndex); // Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread - connect(mDataFilesPage, &DataFilesPage::signalLoadedCellsChanged, mAdvancedPage, - &AdvancedPage::slotLoadedCellsChanged, Qt::QueuedConnection); + connect(mDataFilesPage, &DataFilesPage::signalLoadedCellsChanged, mSettingsPage, + &SettingsPage::slotLoadedCellsChanged, Qt::QueuedConnection); } Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog() @@ -270,7 +224,7 @@ bool Launcher::MainDialog::reloadSettings() if (!setupGraphicsSettings()) return false; - if (!mSettingsPage->loadSettings()) + if (!mImportPage->loadSettings()) return false; if (!mDataFilesPage->loadSettings()) @@ -279,20 +233,50 @@ bool Launcher::MainDialog::reloadSettings() if (!mGraphicsPage->loadSettings()) return false; - if (!mAdvancedPage->loadSettings()) + if (!mSettingsPage->loadSettings()) return false; return true; } -void Launcher::MainDialog::changePage(QListWidgetItem* current, QListWidgetItem* previous) +void Launcher::MainDialog::enableDataPage() { - if (!current) - current = previous; + pagesWidget->setCurrentIndex(0); + mImportPage->resetProgressBar(); + dataAction->setChecked(true); + graphicsAction->setChecked(false); + importAction->setChecked(false); + settingsAction->setChecked(false); +} - int currentIndex = iconWidget->row(current); - pagesWidget->setCurrentIndex(currentIndex); - mSettingsPage->resetProgressBar(); +void Launcher::MainDialog::enableGraphicsPage() +{ + pagesWidget->setCurrentIndex(1); + mImportPage->resetProgressBar(); + dataAction->setChecked(false); + graphicsAction->setChecked(true); + settingsAction->setChecked(false); + importAction->setChecked(false); +} + +void Launcher::MainDialog::enableSettingsPage() +{ + pagesWidget->setCurrentIndex(2); + mImportPage->resetProgressBar(); + dataAction->setChecked(false); + graphicsAction->setChecked(false); + settingsAction->setChecked(true); + importAction->setChecked(false); +} + +void Launcher::MainDialog::enableImportPage() +{ + pagesWidget->setCurrentIndex(3); + mImportPage->resetProgressBar(); + dataAction->setChecked(false); + graphicsAction->setChecked(false); + settingsAction->setChecked(false); + importAction->setChecked(true); } bool Launcher::MainDialog::setupLauncherSettings() @@ -490,8 +474,8 @@ bool Launcher::MainDialog::writeSettings() saveSettings(); mDataFilesPage->saveSettings(); mGraphicsPage->saveSettings(); + mImportPage->saveSettings(); mSettingsPage->saveSettings(); - mAdvancedPage->saveSettings(); const auto& userPath = mCfgMgr.getUserConfigPath(); diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 6644012df8..9f48aedef3 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -20,12 +20,11 @@ class QString; namespace Launcher { - class PlayPage; class GraphicsPage; class DataFilesPage; class UnshieldThread; + class ImportPage; class SettingsPage; - class AdvancedPage; enum FirstRunDialogResult { @@ -52,7 +51,10 @@ namespace Launcher bool writeSettings(); public slots: - void changePage(QListWidgetItem* current, QListWidgetItem* previous); + void enableDataPage(); + void enableGraphicsPage(); + void enableSettingsPage(); + void enableImportPage(); void play(); void help(); @@ -84,11 +86,10 @@ namespace Launcher void closeEvent(QCloseEvent* event) override; - PlayPage* mPlayPage; GraphicsPage* mGraphicsPage; DataFilesPage* mDataFilesPage; + ImportPage* mImportPage; SettingsPage* mSettingsPage; - AdvancedPage* mAdvancedPage; Process::ProcessInvoker* mGameInvoker; Process::ProcessInvoker* mWizardInvoker; diff --git a/apps/launcher/playpage.cpp b/apps/launcher/playpage.cpp deleted file mode 100644 index c3c87e03fb..0000000000 --- a/apps/launcher/playpage.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "playpage.hpp" - -#include - -Launcher::PlayPage::PlayPage(QWidget* parent) - : QWidget(parent) -{ - setObjectName("PlayPage"); - setupUi(this); - - profilesComboBox->setView(new QListView()); - - connect(profilesComboBox, qOverload(&QComboBox::activated), this, &PlayPage::signalProfileChanged); - connect(playButton, &QPushButton::clicked, this, &PlayPage::slotPlayClicked); -} - -void Launcher::PlayPage::setProfilesModel(QAbstractItemModel* model) -{ - profilesComboBox->setModel(model); -} - -void Launcher::PlayPage::setProfilesIndex(int index) -{ - profilesComboBox->setCurrentIndex(index); -} - -void Launcher::PlayPage::slotPlayClicked() -{ - emit playButtonClicked(); -} diff --git a/apps/launcher/playpage.hpp b/apps/launcher/playpage.hpp deleted file mode 100644 index 8e658d6d2c..0000000000 --- a/apps/launcher/playpage.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef PLAYPAGE_H -#define PLAYPAGE_H - -#include "ui_playpage.h" - -class QComboBox; -class QPushButton; -class QAbstractItemModel; - -namespace Launcher -{ - class PlayPage : public QWidget, private Ui::PlayPage - { - Q_OBJECT - - public: - PlayPage(QWidget* parent = nullptr); - void setProfilesModel(QAbstractItemModel* model); - - signals: - void signalProfileChanged(int index); - void playButtonClicked(); - - public slots: - void setProfilesIndex(int index); - - private slots: - void slotPlayClicked(); - }; -} -#endif diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index ea4531cac6..fcfdcb12be 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -1,278 +1,490 @@ #include "settingspage.hpp" -#include -#include +#include +#include +#include + +#include #include -#include +#include -#include +#include -#include "utils/textinputdialog.hpp" +#include "utils/openalutil.hpp" -using namespace Process; - -Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, - Config::LauncherSettings& launcherSettings, MainDialog* parent) +Launcher::SettingsPage::SettingsPage(Config::GameSettings& gameSettings, QWidget* parent) : QWidget(parent) - , mCfgMgr(cfg) , mGameSettings(gameSettings) - , mLauncherSettings(launcherSettings) - , mMain(parent) { + setObjectName("SettingsPage"); setupUi(this); - QStringList languages; - languages << tr("English") << tr("French") << tr("German") << tr("Italian") << tr("Polish") << tr("Russian") - << tr("Spanish"); - - languageComboBox->addItems(languages); - - mWizardInvoker = new ProcessInvoker(); - mImporterInvoker = new ProcessInvoker(); - resetProgressBar(); - - connect(mWizardInvoker->getProcess(), &QProcess::started, this, &SettingsPage::wizardStarted); - - connect(mWizardInvoker->getProcess(), qOverload(&QProcess::finished), this, - &SettingsPage::wizardFinished); - - connect(mImporterInvoker->getProcess(), &QProcess::started, this, &SettingsPage::importerStarted); - - connect(mImporterInvoker->getProcess(), qOverload(&QProcess::finished), this, - &SettingsPage::importerFinished); - - mProfileDialog = new TextInputDialog(tr("New Content List"), tr("Content List name:"), this); - - connect(mProfileDialog->lineEdit(), &LineEdit::textChanged, this, &SettingsPage::updateOkButton); - - // Detect Morrowind configuration files - QStringList iniPaths; - - for (const QString& path : mGameSettings.getDataDirs()) + for (const std::string& name : Launcher::enumerateOpenALDevices()) { - QDir dir(path); - dir.setPath(dir.canonicalPath()); // Resolve symlinks - - if (dir.exists(QString("Morrowind.ini"))) - iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); - else - { - if (!dir.cdUp()) - continue; // Cannot move from Data Files - - if (dir.exists(QString("Morrowind.ini"))) - iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini"))); - } + audioDeviceSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name)); } - - if (!iniPaths.isEmpty()) + for (const std::string& name : Launcher::enumerateOpenALDevicesHrtf()) { - settingsComboBox->addItems(iniPaths); - importerButton->setEnabled(true); - } - else - { - importerButton->setEnabled(false); + hrtfProfileSelectorComboBox->addItem(QString::fromStdString(name), QString::fromStdString(name)); } loadSettings(); + + mCellNameCompleter.setModel(&mCellNameCompleterModel); + startDefaultCharacterAtField->setCompleter(&mCellNameCompleter); } -Launcher::SettingsPage::~SettingsPage() +void Launcher::SettingsPage::loadCellsForAutocomplete(QStringList cellNames) { - delete mWizardInvoker; - delete mImporterInvoker; + // Update the list of suggestions for the "Start default character at" field + mCellNameCompleterModel.setStringList(cellNames); + mCellNameCompleter.setCompletionMode(QCompleter::PopupCompletion); + mCellNameCompleter.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); } -void Launcher::SettingsPage::on_wizardButton_clicked() +void Launcher::SettingsPage::on_skipMenuCheckBox_stateChanged(int state) { - mMain->writeSettings(); - - if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) - return; + startDefaultCharacterAtLabel->setEnabled(state == Qt::Checked); + startDefaultCharacterAtField->setEnabled(state == Qt::Checked); } -void Launcher::SettingsPage::on_importerButton_clicked() +void Launcher::SettingsPage::on_runScriptAfterStartupBrowseButton_clicked() { - mMain->writeSettings(); + QString scriptFile = QFileDialog::getOpenFileName( + this, QObject::tr("Select script file"), QDir::currentPath(), QString(tr("Text file (*.txt)"))); - // Create the file if it doesn't already exist, else the importer will fail - auto path = mCfgMgr.getUserConfigPath(); - path /= "openmw.cfg"; -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QFile file(path); -#else - QFile file(Files::pathToQString(path)); -#endif - - if (!file.exists()) - { - if (!file.open(QIODevice::ReadWrite)) - { - // File cannot be created - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText( - tr("

Could not open or create %1 for writing

\ -

Please make sure you have the right permissions \ - and try again.

") - .arg(file.fileName())); - msgBox.exec(); - return; - } - - file.close(); - } - - // Construct the arguments to run the importer - QStringList arguments; - - if (addonsCheckBox->isChecked()) - arguments.append(QString("--game-files")); - - if (fontsCheckBox->isChecked()) - arguments.append(QString("--fonts")); - - arguments.append(QString("--encoding")); - arguments.append(mGameSettings.value(QString("encoding"), QString("win1252"))); - arguments.append(QString("--ini")); - arguments.append(settingsComboBox->currentText()); - arguments.append(QString("--cfg")); - arguments.append(Files::pathToQString(path)); - - qDebug() << "arguments " << arguments; - - // start the progress bar as a "bouncing ball" - progressBar->setMaximum(0); - progressBar->setValue(0); - if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false)) - { - resetProgressBar(); - } -} - -void Launcher::SettingsPage::on_browseButton_clicked() -{ - QString iniFile = QFileDialog::getOpenFileName(this, QObject::tr("Select configuration file"), QDir::currentPath(), - QString(tr("Morrowind configuration file (*.ini)"))); - - if (iniFile.isEmpty()) + if (scriptFile.isEmpty()) return; - QFileInfo info(iniFile); + QFileInfo info(scriptFile); if (!info.exists() || !info.isReadable()) return; const QString path(QDir::toNativeSeparators(info.absoluteFilePath())); + runScriptAfterStartupField->setText(path); +} - if (settingsComboBox->findText(path) == -1) +namespace +{ + constexpr double CellSizeInUnits = 8192; + + double convertToCells(double unitRadius) { - settingsComboBox->addItem(path); - settingsComboBox->setCurrentIndex(settingsComboBox->findText(path)); - importerButton->setEnabled(true); - } -} - -void Launcher::SettingsPage::wizardStarted() -{ - mMain->hide(); // Hide the launcher - - wizardButton->setEnabled(false); -} - -void Launcher::SettingsPage::wizardFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - if (exitCode != 0 || exitStatus == QProcess::CrashExit) - return qApp->quit(); - - mMain->reloadSettings(); - wizardButton->setEnabled(true); - - mMain->show(); // Show the launcher again -} - -void Launcher::SettingsPage::importerStarted() -{ - importerButton->setEnabled(false); -} - -void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - if (exitCode != 0 || exitStatus == QProcess::CrashExit) - { - resetProgressBar(); - - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Importer finished")); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setText(tr("Failed to import settings from INI file.")); - msgBox.exec(); - } - else - { - // indicate progress finished - progressBar->setMaximum(1); - progressBar->setValue(1); - - // Importer may have changed settings, so refresh - mMain->reloadSettings(); + return unitRadius / CellSizeInUnits; } - importerButton->setEnabled(true); -} - -void Launcher::SettingsPage::resetProgressBar() -{ - // set progress bar to 0 % - progressBar->reset(); -} - -void Launcher::SettingsPage::updateOkButton(const QString& text) -{ - // We do this here because we need to access the profiles - if (text.isEmpty()) + int convertToUnits(double CellGridRadius) { - mProfileDialog->setOkButtonEnabled(false); - return; - } - - const QStringList profiles(mLauncherSettings.getContentLists()); - - (profiles.contains(text)) ? mProfileDialog->setOkButtonEnabled(false) : mProfileDialog->setOkButtonEnabled(true); -} - -void Launcher::SettingsPage::saveSettings() -{ - QString language(languageComboBox->currentText()); - - mLauncherSettings.setValue(QLatin1String("Settings/language"), language); - - if (language == QLatin1String("Polish")) - { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250")); - } - else if (language == QLatin1String("Russian")) - { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251")); - } - else - { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252")); + return static_cast(CellSizeInUnits * CellGridRadius); } } bool Launcher::SettingsPage::loadSettings() { - QString language(mLauncherSettings.value(QLatin1String("Settings/language"))); + // Game mechanics + { + loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); + loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); + loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); + loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); + loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); + loadSettingBool(classicCalmSpellsCheckBox, "classic calm spells behavior", "Game"); + loadSettingBool( + requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game"); + loadSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); + loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); + loadSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); + loadSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); + int unarmedFactorsStrengthIndex = Settings::Manager::getInt("strength influences hand to hand", "Game"); + if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2) + unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex); + loadSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game"); + loadSettingBool(enableNavigatorCheckBox, "enable", "Navigator"); + int numPhysicsThreads = Settings::Manager::getInt("async num threads", "Physics"); + if (numPhysicsThreads >= 0) + physicsThreadsSpinBox->setValue(numPhysicsThreads); + loadSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game"); + loadSettingBool(unarmedCreatureAttacksDamageArmorCheckBox, "unarmed creature attacks damage armor", "Game"); + const int actorCollisionShapeType = Settings::Manager::getInt("actor collision shape type", "Game"); + if (0 <= actorCollisionShapeType && actorCollisionShapeType < actorCollisonShapeTypeComboBox->count()) + actorCollisonShapeTypeComboBox->setCurrentIndex(actorCollisionShapeType); + } - int index = languageComboBox->findText(language); + // Visuals + { + loadSettingBool(autoUseObjectNormalMapsCheckBox, "auto use object normal maps", "Shaders"); + loadSettingBool(autoUseObjectSpecularMapsCheckBox, "auto use object specular maps", "Shaders"); + loadSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders"); + loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders"); + loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders"); + loadSettingBool(softParticlesCheckBox, "soft particles", "Shaders"); + loadSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders"); + if (Settings::Manager::getInt("antialiasing", "Video") == 0) + { + antialiasAlphaTestCheckBox->setCheckState(Qt::Unchecked); + } + loadSettingBool(adjustCoverageForAlphaTestCheckBox, "adjust coverage for alpha test", "Shaders"); + loadSettingBool(weatherParticleOcclusionCheckBox, "weather particle occlusion", "Shaders"); + loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game"); + connect(animSourcesCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotAnimSourcesToggled); + loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); + if (animSourcesCheckBox->checkState() != Qt::Unchecked) + { + loadSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); + loadSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); + } + loadSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); + loadSettingBool(smoothMovementCheckBox, "smooth movement", "Game"); - if (index != -1) - languageComboBox->setCurrentIndex(index); + const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); + const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain"); + if (distantTerrain && objectPaging) + { + distantLandCheckBox->setCheckState(Qt::Checked); + } + loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); + viewingDistanceComboBox->setValue(convertToCells(Settings::Manager::getInt("viewing distance", "Camera"))); + objectPagingMinSizeComboBox->setValue(Settings::Manager::getDouble("object paging min size", "Terrain")); + + loadSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game"); + + connect(postprocessEnabledCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotPostProcessToggled); + loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing"); + loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing"); + postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("auto exposure speed", "Post Processing")); + + connect(skyBlendingCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotSkyBlendingToggled); + loadSettingBool(radialFogCheckBox, "radial fog", "Fog"); + loadSettingBool(exponentialFogCheckBox, "exponential fog", "Fog"); + loadSettingBool(skyBlendingCheckBox, "sky blending", "Fog"); + skyBlendingStartComboBox->setValue(Settings::Manager::getDouble("sky blending start", "Fog")); + } + + // Audio + { + const std::string& selectedAudioDevice = Settings::Manager::getString("device", "Sound"); + if (selectedAudioDevice.empty() == false) + { + int audioDeviceIndex = audioDeviceSelectorComboBox->findData(QString::fromStdString(selectedAudioDevice)); + if (audioDeviceIndex != -1) + { + audioDeviceSelectorComboBox->setCurrentIndex(audioDeviceIndex); + } + } + int hrtfEnabledIndex = Settings::Manager::getInt("hrtf enable", "Sound"); + if (hrtfEnabledIndex >= -1 && hrtfEnabledIndex <= 1) + { + enableHRTFComboBox->setCurrentIndex(hrtfEnabledIndex + 1); + } + const std::string& selectedHRTFProfile = Settings::Manager::getString("hrtf", "Sound"); + if (selectedHRTFProfile.empty() == false) + { + int hrtfProfileIndex = hrtfProfileSelectorComboBox->findData(QString::fromStdString(selectedHRTFProfile)); + if (hrtfProfileIndex != -1) + { + hrtfProfileSelectorComboBox->setCurrentIndex(hrtfProfileIndex); + } + } + } + + // Interface Changes + { + loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game"); + loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game"); + loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); + loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); + loadSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); + int showOwnedIndex = Settings::Manager::getInt("show owned", "Game"); + // Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid. + if (showOwnedIndex >= 0 && showOwnedIndex <= 3) + showOwnedComboBox->setCurrentIndex(showOwnedIndex); + loadSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); + loadSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map"); + loadSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); + scalingSpinBox->setValue(Settings::Manager::getFloat("scaling factor", "GUI")); + fontSizeSpinBox->setValue(Settings::Manager::getInt("font size", "GUI")); + } + + // Bug fixes + { + loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); + loadSettingBool( + trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); + } + + // Miscellaneous + { + // Saves + loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); + loadSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves"); + + // Other Settings + QString screenshotFormatString + = QString::fromStdString(Settings::Manager::getString("screenshot format", "General")).toUpper(); + if (screenshotFormatComboBox->findText(screenshotFormatString) == -1) + screenshotFormatComboBox->addItem(screenshotFormatString); + screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString)); + + loadSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General"); + } + + // Testing + { + loadSettingBool(grabCursorCheckBox, "grab cursor", "Input"); + + bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1; + if (skipMenu) + { + skipMenuCheckBox->setCheckState(Qt::Checked); + } + startDefaultCharacterAtLabel->setEnabled(skipMenu); + startDefaultCharacterAtField->setEnabled(skipMenu); + + startDefaultCharacterAtField->setText(mGameSettings.value("start")); + runScriptAfterStartupField->setText(mGameSettings.value("script-run")); + } return true; } + +void Launcher::SettingsPage::saveSettings() +{ + // Game mechanics + { + saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game"); + saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game"); + saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game"); + saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game"); + saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game"); + saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game"); + saveSettingBool(classicCalmSpellsCheckBox, "classic calm spells behavior", "Game"); + saveSettingBool( + requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game"); + saveSettingBool(uncappedDamageFatigueCheckBox, "uncapped damage fatigue", "Game"); + saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game"); + saveSettingBool(swimUpwardCorrectionCheckBox, "swim upward correction", "Game"); + saveSettingBool(avoidCollisionsCheckBox, "NPCs avoid collisions", "Game"); + saveSettingInt(unarmedFactorsStrengthComboBox, "strength influences hand to hand", "Game"); + saveSettingBool(stealingFromKnockedOutCheckBox, "always allow stealing from knocked out actors", "Game"); + saveSettingBool(enableNavigatorCheckBox, "enable", "Navigator"); + saveSettingInt(physicsThreadsSpinBox, "async num threads", "Physics"); + saveSettingBool(allowNPCToFollowOverWaterSurfaceCheckBox, "allow actors to follow over water surface", "Game"); + saveSettingBool(unarmedCreatureAttacksDamageArmorCheckBox, "unarmed creature attacks damage armor", "Game"); + saveSettingInt(actorCollisonShapeTypeComboBox, "actor collision shape type", "Game"); + } + + // Visuals + { + saveSettingBool(autoUseObjectNormalMapsCheckBox, "auto use object normal maps", "Shaders"); + saveSettingBool(autoUseObjectSpecularMapsCheckBox, "auto use object specular maps", "Shaders"); + saveSettingBool(autoUseTerrainNormalMapsCheckBox, "auto use terrain normal maps", "Shaders"); + saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders"); + saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders"); + saveSettingBool(radialFogCheckBox, "radial fog", "Fog"); + saveSettingBool(softParticlesCheckBox, "soft particles", "Shaders"); + saveSettingBool(antialiasAlphaTestCheckBox, "antialias alpha test", "Shaders"); + saveSettingBool(adjustCoverageForAlphaTestCheckBox, "adjust coverage for alpha test", "Shaders"); + saveSettingBool(weatherParticleOcclusionCheckBox, "weather particle occlusion", "Shaders"); + saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game"); + saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game"); + saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game"); + saveSettingBool(shieldSheathingCheckBox, "shield sheathing", "Game"); + saveSettingBool(turnToMovementDirectionCheckBox, "turn to movement direction", "Game"); + saveSettingBool(smoothMovementCheckBox, "smooth movement", "Game"); + + const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); + const bool objectPaging = Settings::Manager::getBool("object paging", "Terrain"); + const bool wantDistantLand = distantLandCheckBox->checkState(); + if (wantDistantLand != (distantTerrain && objectPaging)) + { + Settings::Manager::setBool("distant terrain", "Terrain", wantDistantLand); + Settings::Manager::setBool("object paging", "Terrain", wantDistantLand); + } + + saveSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain"); + int viewingDistance = convertToUnits(viewingDistanceComboBox->value()); + if (viewingDistance != Settings::Manager::getInt("viewing distance", "Camera")) + { + Settings::Manager::setInt("viewing distance", "Camera", viewingDistance); + } + double objectPagingMinSize = objectPagingMinSizeComboBox->value(); + if (objectPagingMinSize != Settings::Manager::getDouble("object paging min size", "Terrain")) + Settings::Manager::setDouble("object paging min size", "Terrain", objectPagingMinSize); + + saveSettingBool(nightDaySwitchesCheckBox, "day night switches", "Game"); + + saveSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing"); + saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing"); + double hdrExposureTime = postprocessHDRTimeComboBox->value(); + if (hdrExposureTime != Settings::Manager::getDouble("auto exposure speed", "Post Processing")) + Settings::Manager::setDouble("auto exposure speed", "Post Processing", hdrExposureTime); + + saveSettingBool(radialFogCheckBox, "radial fog", "Fog"); + saveSettingBool(exponentialFogCheckBox, "exponential fog", "Fog"); + saveSettingBool(skyBlendingCheckBox, "sky blending", "Fog"); + Settings::Manager::setDouble("sky blending start", "Fog", skyBlendingStartComboBox->value()); + } + + // Audio + { + int audioDeviceIndex = audioDeviceSelectorComboBox->currentIndex(); + const std::string& prevAudioDevice = Settings::Manager::getString("device", "Sound"); + if (audioDeviceIndex != 0) + { + const std::string& newAudioDevice = audioDeviceSelectorComboBox->currentText().toUtf8().constData(); + if (newAudioDevice != prevAudioDevice) + Settings::Manager::setString("device", "Sound", newAudioDevice); + } + else if (!prevAudioDevice.empty()) + { + Settings::Manager::setString("device", "Sound", {}); + } + int hrtfEnabledIndex = enableHRTFComboBox->currentIndex() - 1; + if (hrtfEnabledIndex != Settings::Manager::getInt("hrtf enable", "Sound")) + { + Settings::Manager::setInt("hrtf enable", "Sound", hrtfEnabledIndex); + } + int selectedHRTFProfileIndex = hrtfProfileSelectorComboBox->currentIndex(); + const std::string& prevHRTFProfile = Settings::Manager::getString("hrtf", "Sound"); + if (selectedHRTFProfileIndex != 0) + { + const std::string& newHRTFProfile = hrtfProfileSelectorComboBox->currentText().toUtf8().constData(); + if (newHRTFProfile != prevHRTFProfile) + Settings::Manager::setString("hrtf", "Sound", newHRTFProfile); + } + else if (!prevHRTFProfile.empty()) + { + Settings::Manager::setString("hrtf", "Sound", {}); + } + } + + // Interface Changes + { + saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game"); + saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game"); + saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game"); + saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game"); + saveSettingBool(changeDialogTopicsCheckBox, "color topic enable", "GUI"); + saveSettingInt(showOwnedComboBox, "show owned", "Game"); + saveSettingBool(stretchBackgroundCheckBox, "stretch menu background", "GUI"); + saveSettingBool(useZoomOnMapCheckBox, "allow zooming", "Map"); + saveSettingBool(graphicHerbalismCheckBox, "graphic herbalism", "Game"); + + float uiScalingFactor = scalingSpinBox->value(); + if (uiScalingFactor != Settings::Manager::getFloat("scaling factor", "GUI")) + Settings::Manager::setFloat("scaling factor", "GUI", uiScalingFactor); + + int fontSize = fontSizeSpinBox->value(); + if (fontSize != Settings::Manager::getInt("font size", "GUI")) + Settings::Manager::setInt("font size", "GUI", fontSize); + } + + // Bug fixes + { + saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game"); + saveSettingBool( + trainersTrainingSkillsBasedOnBaseSkillCheckBox, "trainers training skills based on base skill", "Game"); + } + + // Miscellaneous + { + // Saves Settings + saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves"); + saveSettingInt(maximumQuicksavesComboBox, "max quicksaves", "Saves"); + + // Other Settings + std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString(); + if (screenshotFormatString != Settings::Manager::getString("screenshot format", "General")) + Settings::Manager::setString("screenshot format", "General", screenshotFormatString); + + saveSettingBool(notifyOnSavedScreenshotCheckBox, "notify on saved screenshot", "General"); + } + + // Testing + { + saveSettingBool(grabCursorCheckBox, "grab cursor", "Input"); + + int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked; + if (skipMenu != mGameSettings.value("skip-menu").toInt()) + mGameSettings.setValue("skip-menu", QString::number(skipMenu)); + + QString startCell = startDefaultCharacterAtField->text(); + if (startCell != mGameSettings.value("start")) + { + mGameSettings.setValue("start", startCell); + } + QString scriptRun = runScriptAfterStartupField->text(); + if (scriptRun != mGameSettings.value("script-run")) + mGameSettings.setValue("script-run", scriptRun); + } +} + +void Launcher::SettingsPage::loadSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group) +{ + if (Settings::Manager::getBool(setting, group)) + checkbox->setCheckState(Qt::Checked); +} + +void Launcher::SettingsPage::saveSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group) +{ + bool cValue = checkbox->checkState(); + if (cValue != Settings::Manager::getBool(setting, group)) + Settings::Manager::setBool(setting, group, cValue); +} + +void Launcher::SettingsPage::loadSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group) +{ + int currentIndex = Settings::Manager::getInt(setting, group); + comboBox->setCurrentIndex(currentIndex); +} + +void Launcher::SettingsPage::saveSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group) +{ + int currentIndex = comboBox->currentIndex(); + if (currentIndex != Settings::Manager::getInt(setting, group)) + Settings::Manager::setInt(setting, group, currentIndex); +} + +void Launcher::SettingsPage::loadSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group) +{ + int value = Settings::Manager::getInt(setting, group); + spinBox->setValue(value); +} + +void Launcher::SettingsPage::saveSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group) +{ + int value = spinBox->value(); + if (value != Settings::Manager::getInt(setting, group)) + Settings::Manager::setInt(setting, group, value); +} + +void Launcher::SettingsPage::slotLoadedCellsChanged(QStringList cellNames) +{ + loadCellsForAutocomplete(cellNames); +} + +void Launcher::SettingsPage::slotAnimSourcesToggled(bool checked) +{ + weaponSheathingCheckBox->setEnabled(checked); + shieldSheathingCheckBox->setEnabled(checked); + if (!checked) + { + weaponSheathingCheckBox->setCheckState(Qt::Unchecked); + shieldSheathingCheckBox->setCheckState(Qt::Unchecked); + } +} + +void Launcher::SettingsPage::slotPostProcessToggled(bool checked) +{ + postprocessTransparentPostpassCheckBox->setEnabled(checked); + postprocessHDRTimeComboBox->setEnabled(checked); + postprocessHDRTimeLabel->setEnabled(checked); +} + +void Launcher::SettingsPage::slotSkyBlendingToggled(bool checked) +{ + skyBlendingStartComboBox->setEnabled(checked); + skyBlendingStartLabel->setEnabled(checked); +} diff --git a/apps/launcher/settingspage.hpp b/apps/launcher/settingspage.hpp index 8e5eefd306..ac769e4827 100644 --- a/apps/launcher/settingspage.hpp +++ b/apps/launcher/settingspage.hpp @@ -1,67 +1,56 @@ -#ifndef SETTINGSPAGE_HPP -#define SETTINGSPAGE_HPP +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H -#include +#include +#include #include "ui_settingspage.h" -#include "maindialog.hpp" +#include -namespace Files -{ - struct ConfigurationManager; -} namespace Config { class GameSettings; - class LauncherSettings; } namespace Launcher { - class TextInputDialog; - class SettingsPage : public QWidget, private Ui::SettingsPage { Q_OBJECT public: - SettingsPage(Files::ConfigurationManager& cfg, Config::GameSettings& gameSettings, - Config::LauncherSettings& launcherSettings, MainDialog* parent = nullptr); - ~SettingsPage() override; + explicit SettingsPage(Config::GameSettings& gameSettings, QWidget* parent = nullptr); - void saveSettings(); bool loadSettings(); + void saveSettings(); - /// set progress bar on page to 0% - void resetProgressBar(); + public slots: + void slotLoadedCellsChanged(QStringList cellNames); private slots: - - void on_wizardButton_clicked(); - void on_importerButton_clicked(); - void on_browseButton_clicked(); - - void wizardStarted(); - void wizardFinished(int exitCode, QProcess::ExitStatus exitStatus); - - void importerStarted(); - void importerFinished(int exitCode, QProcess::ExitStatus exitStatus); - - void updateOkButton(const QString& text); + void on_skipMenuCheckBox_stateChanged(int state); + void on_runScriptAfterStartupBrowseButton_clicked(); + void slotAnimSourcesToggled(bool checked); + void slotPostProcessToggled(bool checked); + void slotSkyBlendingToggled(bool checked); private: - Process::ProcessInvoker* mWizardInvoker; - Process::ProcessInvoker* mImporterInvoker; - - Files::ConfigurationManager& mCfgMgr; - Config::GameSettings& mGameSettings; - Config::LauncherSettings& mLauncherSettings; + QCompleter mCellNameCompleter; + QStringListModel mCellNameCompleterModel; - MainDialog* mMain; - TextInputDialog* mProfileDialog; + /** + * Load the cells associated with the given content files for use in autocomplete + * @param filePaths the file paths of the content files to be examined + */ + void loadCellsForAutocomplete(QStringList filePaths); + static void loadSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group); + static void saveSettingBool(QCheckBox* checkbox, const std::string& setting, const std::string& group); + static void loadSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group); + static void saveSettingInt(QComboBox* comboBox, const std::string& setting, const std::string& group); + static void loadSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group); + static void saveSettingInt(QSpinBox* spinBox, const std::string& setting, const std::string& group); }; } - -#endif // SETTINGSPAGE_HPP +#endif diff --git a/apps/launcher/utils/lineedit.cpp b/apps/launcher/utils/lineedit.cpp index 011acd7699..0741e84c12 100644 --- a/apps/launcher/utils/lineedit.cpp +++ b/apps/launcher/utils/lineedit.cpp @@ -9,9 +9,7 @@ LineEdit::LineEdit(QWidget* parent) void LineEdit::setupClearButton() { mClearButton = new QToolButton(this); - QPixmap pixmap(":images/clear.png"); - mClearButton->setIcon(QIcon(pixmap)); - mClearButton->setIconSize(pixmap.size()); + mClearButton->setIcon(QIcon::fromTheme("edit-clear")); mClearButton->setCursor(Qt::ArrowCursor); mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }"); mClearButton->hide(); diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 2cfae983ae..937309a864 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -13,6 +13,12 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget* parent, bool show ui.setupUi(parent); ui.addonView->setDragDropMode(QAbstractItemView::InternalMove); + if (!showOMWScripts) + { + ui.languageComboBox->setHidden(true); + ui.refreshButton->setHidden(true); + } + buildContentModel(showOMWScripts); buildGameFileView(); buildAddonView(); diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 98131ae52d..5919d2e516 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -43,6 +43,8 @@ namespace ContentSelectorView QWidget* uiWidget() const { return ui.contentGroupBox; } + QComboBox* languageBox() const { return ui.languageComboBox; } + QToolButton* refreshButton() const { return ui.refreshButton; } QLineEdit* searchFilter() const { return ui.searchFilter; } diff --git a/files/launcher/images/clear.png b/files/launcher/icons/tango/16x16/edit-clear.png similarity index 100% rename from files/launcher/images/clear.png rename to files/launcher/icons/tango/16x16/edit-clear.png diff --git a/files/launcher/icons/tango/16x16/go-bottom.png b/files/launcher/icons/tango/16x16/go-bottom.png deleted file mode 100644 index 2c5a80385c..0000000000 Binary files a/files/launcher/icons/tango/16x16/go-bottom.png and /dev/null differ diff --git a/files/launcher/icons/tango/16x16/go-down.png b/files/launcher/icons/tango/16x16/go-down.png deleted file mode 100644 index 3dd7fccdf0..0000000000 Binary files a/files/launcher/icons/tango/16x16/go-down.png and /dev/null differ diff --git a/files/launcher/icons/tango/16x16/go-top.png b/files/launcher/icons/tango/16x16/go-top.png deleted file mode 100644 index 70f2c996cd..0000000000 Binary files a/files/launcher/icons/tango/16x16/go-top.png and /dev/null differ diff --git a/files/launcher/icons/tango/16x16/go-up.png b/files/launcher/icons/tango/16x16/go-up.png deleted file mode 100644 index fa9a7d71b5..0000000000 Binary files a/files/launcher/icons/tango/16x16/go-up.png and /dev/null differ diff --git a/files/launcher/icons/tango/16x16/view-refresh.png b/files/launcher/icons/tango/16x16/view-refresh.png new file mode 100644 index 0000000000..3fd71d6e59 Binary files /dev/null and b/files/launcher/icons/tango/16x16/view-refresh.png differ diff --git a/files/launcher/icons/tango/48x48/emblem-system.png b/files/launcher/icons/tango/48x48/emblem-system.png deleted file mode 100644 index 9a765247ca..0000000000 Binary files a/files/launcher/icons/tango/48x48/emblem-system.png and /dev/null differ diff --git a/files/launcher/icons/tango/48x48/preferences-system.png b/files/launcher/icons/tango/48x48/preferences-system.png deleted file mode 100644 index a1620e9916..0000000000 Binary files a/files/launcher/icons/tango/48x48/preferences-system.png and /dev/null differ diff --git a/files/launcher/icons/tango/48x48/video-display.png b/files/launcher/icons/tango/48x48/video-display.png deleted file mode 100644 index 1331436846..0000000000 Binary files a/files/launcher/icons/tango/48x48/video-display.png and /dev/null differ diff --git a/files/launcher/icons/tango/index.theme b/files/launcher/icons/tango/index.theme index 8ec560f856..1f54489ebb 100644 --- a/files/launcher/icons/tango/index.theme +++ b/files/launcher/icons/tango/index.theme @@ -2,10 +2,7 @@ Name=Tango Comment=Tango Theme Inherits=default -Directories=16x16,48x48 +Directories=16x16 [16x16] -Size=16 - -[48x48] -Size=48 \ No newline at end of file +Size=16 \ No newline at end of file diff --git a/files/launcher/images/down.png b/files/launcher/images/down.png deleted file mode 100644 index 9219cc6ed3..0000000000 Binary files a/files/launcher/images/down.png and /dev/null differ diff --git a/files/launcher/images/openmw-header.png b/files/launcher/images/openmw-header.png old mode 100644 new mode 100755 index 3a82e471f6..6c88d8f549 Binary files a/files/launcher/images/openmw-header.png and b/files/launcher/images/openmw-header.png differ diff --git a/files/launcher/images/playpage-background.png b/files/launcher/images/playpage-background.png deleted file mode 100644 index b733ecf808..0000000000 Binary files a/files/launcher/images/playpage-background.png and /dev/null differ diff --git a/files/launcher/launcher.qrc b/files/launcher/launcher.qrc index 17c38a838b..766f636468 100644 --- a/files/launcher/launcher.qrc +++ b/files/launcher/launcher.qrc @@ -1,26 +1,18 @@ - images/clear.png - images/down.png images/openmw.png - images/openmw-plugin.png images/openmw-header.png + images/openmw-plugin.png images/preferences.png images/preferences-advanced.png images/preferences-video.png - images/playpage-background.png icons/tango/index.theme - icons/tango/48x48/emblem-system.png - icons/tango/48x48/preferences-system.png - icons/tango/48x48/video-display.png icons/tango/16x16/document-new.png + icons/tango/16x16/edit-clear.png icons/tango/16x16/edit-copy.png icons/tango/16x16/edit-delete.png - icons/tango/16x16/go-bottom.png - icons/tango/16x16/go-down.png - icons/tango/16x16/go-top.png - icons/tango/16x16/go-up.png + icons/tango/16x16/view-refresh.png diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui deleted file mode 100644 index 91f38b2ffb..0000000000 --- a/files/ui/advancedpage.ui +++ /dev/null @@ -1,1371 +0,0 @@ - - - AdvancedPage - - - - 0 - 0 - 732 - 487 - - - - - - - 0 - - - - Game Mechanics - - - - - - - - <html><head/><body><p>Make enchanted weaponry without Magical flag bypass normal weapons resistance, like in Morrowind.</p></body></html> - - - Enchanted weapons are magical - - - - - - - <html><head/><body><p>Makes player swim a bit upward from the line of sight. Applies only in third person mode. Intended to make simpler swimming without diving.</p></body></html> - - - Swim upward correction - - - - - - - <html><head/><body><p>If enabled NPCs apply evasion maneuver to avoid collisions with others.</p></body></html> - - - NPCs avoid collisions - - - - - - - <html><head/><body><p>Enable navigator. When enabled background threads are started to build nav mesh for world geometry. Pathfinding system uses nav mesh to build paths. When disabled only pathgrid is used to build paths. Single-core CPU systems may have big performance impact on exiting interior location and moving across exterior world. May slightly affect performance on multi-core CPU systems. Multi-core CPU systems may have different latency for nav mesh update depending on other settings and system performance. Moving across external world, entering/exiting location produce nav mesh update. NPC and creatures may not be able to find path before nav mesh is built around them. Try to disable this if you want to have old fashioned AI which doesn’t know where to go when you stand behind that stone and casting a firebolt.</p></body></html> - - - Build nav mesh for world geometry - - - - - - - <html><head/><body><p>This setting causes the behavior of the sneak key (bound to Ctrl by default) to toggle sneaking on and off rather than requiring the key to be held down while sneaking. Players that spend significant time sneaking may find the character easier to control with this option enabled. </p></body></html> - - - <s>Toggle sneak</s> (moved to the in-game menu) - - - - - - - <html><head/><body><p>Make disposition change of merchants caused by trading permanent.</p></body></html> - - - Permanent barter disposition changes - - - - - - - <html><head/><body><p>Make stealing items from NPCs that were knocked down possible during combat.</p></body></html> - - - Always allow stealing from knocked out actors - - - - - - - <html><head/><body><p>Don't use race weight in NPC movement speed calculations.</p></body></html> - - - Racial variation in speed fix - - - - - - - <html><head/><body><p>Make Damage Fatigue magic effect uncapped like Drain Fatigue effect.</p><p>This means that unlike Morrowind you will be able to knock down actors using this effect.</p></body></html> - - - Uncapped Damage Fatigue - - - - - - - <html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.</p><p>If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.</p></body></html> - - - Can loot during death animation - - - - - - - <html><head/><body><p>Make the value of filled soul gems dependent only on soul magnitude.</p></body></html> - - - Soulgem values rebalance - - - - - - - <html><head/><body><p>Make player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.</p></body></html> - - - Followers defend immediately - - - - - - - <html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html> - - - Classic reflected Absorb spells behavior - - - - - - - <html><head/><body><p>Stops combat with NPCs affected by Calm spells every frame -- like in Morrowind without the MCP.</p></body></html> - - - Classic Calm spells behavior - - - - - - - - - <html><head/><body><p>Give NPC an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled.</p></body></html> - - - Always allow NPC to follow over water surface - - - - - - - <html><head/><body><p>Allow non-standard ammunition solely to bypass normal weapon resistance or weakness.</p></body></html> - - - Only appropriate ammunition bypasses normal weapon resistance - - - - - - - <html><head/><body><p>Makes unarmed creature attacks able to reduce armor condition, just as attacks from NPCs and armed creatures.</p></body></html> - - - Unarmed creature attacks damage armor - - - - - - - - - Factor strength into hand-to-hand combat: - - - - - - - 0 - - - - Off - - - - - Affect werewolves - - - - - Do not affect werewolves - - - - - - - - <html><head/><body><p>How many threads will be spawned to compute physics update in the background. A value of 0 means that the update will be performed in the main thread.</p><p>A value greater than 1 requires the Bullet library be compiled with multithreading support.</p></body></html> - - - Background physics threads: - - - - - - - - - - Actor collision shape type: - - - - - - - Collision is used for both physics simulation and navigation mesh generation for pathfinding. Cylinder gives the best consistency bewtween available navigation paths and ability to move by them. Changing this value affects navigation mesh generation therefore navigation mesh disk cache generated for one value will not be useful with another. - - - Axis-aligned bounding box - - - - Axis-aligned bounding box - - - - - Rotating box - - - - - Cylinder - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Visuals - - - - - - true - - - - - 0 - 0 - 665 - 525 - - - - - - - Animations - - - - - - <html><head/><body><p>Use casting animations for magic items, just as for spells.</p></body></html> - - - Use magic item animation - - - - - - - <html><head/><body><p>Makes NPCs and player movement more smooth. Recommended to use with "turn to movement direction" enabled.</p></body></html> - - - Smooth movement - - - - - - - <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> - - - Use additional animation sources - - - - - - - <html><head/><body><p>Affects side and diagonal movement. Enabling this setting makes movement more realistic.</p><p>If disabled then the whole character's body is pointed to the direction of view. Diagonal movement has no special animation and causes sliding.</p><p>If enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it changes straight right and straight left movement as well. Also turns the whole body up or down when swimming according to the movement direction.</p></body></html> - - - Turn to movement direction - - - - - - - 20 - - - - - false - - - <html><head/><body><p>Render holstered weapons (with quivers and scabbards), requires modded assets.</p></body></html> - - - Weapon sheathing - - - - - - - false - - - <html><head/><body><p>Render holstered shield, requires modded assets.</p></body></html> - - - Shield sheathing - - - - - - - - - - - - Shaders - - - - - - <html><head/><body><p>If this option is enabled, normal maps are automatically recognized and used if they are named appropriately - (see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds). - If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file). Affects objects.</p></body></html> - - - Auto use object normal maps - - - - - - - <html><head/><body><p>See 'auto use object normal maps'. Affects terrain.</p></body></html> - - - Auto use terrain normal maps - - - - - - - <html><head/><body><p>If this option is enabled, specular maps are automatically recognized and used if they are named appropriately - (see 'specular map pattern', e.g. for a base texture foo.dds, - the specular map texture would have to be named foo_spec.dds). - If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file - (.osg file, not supported in .nif files). Affects objects.</p></body></html> - - - Auto use object specular maps - - - - - - - <html><head/><body><p>If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. The texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel.</p></body></html> - - - Auto use terrain specular maps - - - - - - - <html><head/><body><p>Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. - Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. - Affected objects will use shaders. - </p></body></html> - - - Bump/reflect map local lighting - - - - - - - <html><head/><body><p>Allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. Can negatively impact performance.</p></body></html> - - - Use anti-alias alpha testing - - - - - - - <html><head/><body><p>Enables soft particles for particle effects. This technique softens the intersection between individual particles and other opaque geometry by blending between them.</p></body></html> - - - Soft Particles - - - - - - - <html><head/><body><p>Simulate coverage-preserving mipmaps to prevent alpha-tested meshes shrinking as they get further away. Will cause meshes whose textures have coverage-preserving mipmaps to grow, though, so refer to mod installation instructions for how to set this.</p></body></html> - - - Adjust coverage for alpha test - - - - - - - <html><head/><body><p>EXPERIMENTAL: Stop rain and snow from falling through overhangs and roofs.</p></body></html> - - - Weather Particle Occlusion - - - - - - - - - - Fog - - - - - - <html><head/><body><p>By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. - This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.</p></body></html> - - - Radial fog - - - - - - - <html><head/><body><p>Use exponential fog formula. By default, linear fog is used.</p></body></html> - - - Exponential fog - - - - - - - <html><head/><body><p>Reduce visibility of clipping plane by blending objects with the sky.</p></body></html> - - - Sky blending - - - - - - - false - - - Sky blending start - - - <html><head/><body><p>The fraction of the maximum distance at which blending with the sky starts.</p></body></html> - - - - - - - false - - - 3 - - - 0.000000000000000 - - - 1.000000000000000 - - - 0.005000000000000 - - - - - - - - - - Terrain - - - - - - - - Viewing distance - - - - - - - Cells - - - 0.000000000000000 - - - 0.500000000000000 - - - - - - - - - - - <html><head/><body><p>Controls how large an object must be to be visible in the scene. The object’s size is divided by its distance to the camera and the result of the division is compared with this value. The smaller this value is, the more objects you will see in the scene.</p></body></html> - - - Object paging min size - - - - - - - 3 - - - 0.000000000000000 - - - 0.250000000000000 - - - 0.005000000000000 - - - - - - - - - <html><head/><body><p>If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.</p></body></html> - - - Distant land - - - - - - - 20 - - - - - <html><head/><body><p>Use object paging for active cells grid.</p></body></html> - - - Active grid object paging - - - - - - - - - - - - Models - - - - - - <html><head/><body><p>If this setting is true, supporting models will make use of day night switch nodes.</p></body></html> - - - Day night switch nodes - - - - - - - - - - Post Processing - - - - - - <html><head/><body><p>If this setting is true, post processing will be enabled.</p></body></html> - - - Enable post processing - - - - - - - 20 - - - - - false - - - <html><head/><body><p>Re-render transparent objects with forced alpha clipping.</p></body></html> - - - Transparent postpass - - - - - - - - - false - - - <html><head/><body><p>Controls how much eye adaptation can change from frame to frame. Smaller values makes for slower transitions.</p></body></html> - - - Auto exposure speed - - - - - - - false - - - 3 - - - 0.010000000000000 - - - 10.000000000000000 - - - 0.001000000000000 - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - Audio - - - - - - - - Select your preferred audio device. - - - Audio Device - - - - - - - - 0 - 0 - - - - - 283 - 0 - - - - 0 - - - - Default - - - - - - - - - - - - This setting controls HRTF, which simulates 3D sound on stereo systems. - - - HRTF - - - - - - - - 0 - 0 - - - - - 283 - 0 - - - - 0 - - - - Automatic - - - - - Off - - - - - On - - - - - - - - - - - - Select your preferred HRTF profile. - - - HRTF Profile - - - - - - - - 0 - 0 - - - - - 283 - 0 - - - - 0 - - - - Default - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - Interface - - - - - - - - 1 - - - - Off - - - - - Tool Tip Only - - - - - Crosshair Only - - - - - Tool Tip and Crosshair - - - - - - - - 2 - - - 0.500000000000000 - - - 8.000000000000000 - - - 0.250000000000000 - - - 1.000000000000000 - - - - - - - 12 - - - 18 - - - 1 - - - 16 - - - - - - - <html><head/><body><p>This setting scales GUI windows. A value of 1.0 results in the normal scale.</p></body></html> - - - GUI scaling factor - - - - - - - <html><head/><body><p>Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. </p><p>The default value is false.</p></body></html> - - - Show effect duration - - - - - - - <html><head/><body><p>If this setting is true, dialogue topics will have a different color if the topic is specific to the NPC you're talking to or the topic was previously seen. Color can be changed in settings.cfg.</p><p>The default value is false.</p></body></html> - - - Change dialogue topic color - - - - - - - Size of characters in game texts. - - - Font size - - - - - - - <html><head/><body><p>Enable zooming on local and global maps.</p></body></html> - - - Can zoom on maps - - - - - - - <html><head/><body><p>If this setting is true, containers supporting graphic herbalism will do so instead of opening the menu.</p></body></html> - - - Enable graphic herbalism - - - - - - - <html><head/><body><p>If this setting is true, damage bonus of arrows and bolts will be shown on item tooltip.</p><p>The default value is false.</p></body></html> - - - Show projectile damage - - - - - - - <html><head/><body><p>If this setting is true, melee weapons reach and speed will be shown on item tooltip.</p><p>The default value is false.</p></body></html> - - - Show melee info - - - - - - - <html><head/><body><p>Stretch menus, load screens, etc. to the window aspect ratio.</p></body></html> - - - Stretch menu background - - - - - - - Show owned objects - - - - - - - <html><head/><body><p>Whether or not the chance of success will be displayed in the enchanting menu.</p><p>The default value is false.</p></body></html> - - - Show enchant chance - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Bug Fixes - - - - - - <html><head/><body><p>Prevents merchants from equipping items that are sold to them.</p></body></html> - - - Merchant equipping fix - - - - - - - <html><head/><body><p>Trainers now only choose which skills to train using their base skill points, allowing mercantile improving effects to be used without making mercantile an offered skill.</p></body></html> - - - Trainers choose their training skills based on their base skill points - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - Miscellaneous - - - - - - Saves - - - - - - <html><head/><body><p>This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.</p></body></html> - - - Add "Time Played" to saves - - - - - - - - - Maximum Quicksaves - - - - - - - 1 - - - - - - - - - - - - Other - - - - - - - - Screenshot Format - - - - - - - - JPG - - - - - PNG - - - - - TGA - - - - - - - - - - Notify on saved screenshot - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - Testing - - - - - - These settings are intended for testing mods and will cause issues if used for normal gameplay. - - - true - - - - - - - Qt::Horizontal - - - - - - - <html><head/><body><p>OpenMW will capture control of the cursor if this setting is true.</p><p>In “look mode”, OpenMW will center the cursor regardless of the value of this setting (since the cursor/crosshair is always centered in the OpenMW window). However, in GUI mode, this setting determines the behavior when the cursor is moved outside the OpenMW window. If true, the cursor movement stops at the edge of the window preventing access to other applications. If false, the cursor is allowed to move freely on the desktop.</p><p>This setting does not apply to the screen where escape has been pressed, where the cursor is never captured. Regardless of this setting “Alt-Tab” or some other operating system dependent key sequence can be used to allow the operating system to regain control of the mouse cursor. This setting interacts with the minimize on focus loss setting by affecting what counts as a focus loss. Specifically on a two-screen configuration it may be more convenient to access the second screen with setting disabled.</p><p>Note for developers: it’s desirable to have this setting disabled when running the game in a debugger, to prevent the mouse cursor from becoming unusable when the game pauses on a breakpoint.</p></body></html> - - - Grab cursor - - - - - - - Skip menu and generate default character - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 0 - 0 - - - - - - - - Start default character at - - - - - - - default cell - - - - - - - - - Run script after startup: - - - - - - - - - - - - Browse… - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - - - - - - - - - diff --git a/files/ui/contentselector.ui b/files/ui/contentselector.ui index f19e2ba487..8a311bfaee 100644 --- a/files/ui/contentselector.ui +++ b/files/ui/contentselector.ui @@ -6,7 +6,7 @@ 0 0 - 518 + 614 436 @@ -66,6 +66,19 @@ + + + + + 100 + 0 + + + + Select language used by ESM/ESP content files to allow OpenMW to detect their encoding. + + + diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index c2cbfe6f2e..529f0ca8f2 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -48,57 +48,7 @@ - - - - Scan directories for likely data directories and append them at the end of the list. - - - Append - - - - - - - Scan directories for likely data directories and insert them above the selected position - - - Insert Above - - - - - - - Move selected directory one position up - - - Move Up - - - - - - - Move selected directory one position down - - - Move Down - - - - - - - Remove selected directory - - - Remove - - - - + @@ -111,6 +61,116 @@ + + + + + 0 + 33 + + + + + 0 + 33 + + + + Scan directories for likely data directories and append them at the end of the list. + + + Append + + + + + + + + 0 + 33 + + + + + 0 + 33 + + + + Scan directories for likely data directories and insert them above the selected position + + + Insert Above + + + + + + + + 0 + 33 + + + + + 0 + 33 + + + + Move selected directory one position up + + + Move Up + + + + + + + + 0 + 33 + + + + + 0 + 33 + + + + Move selected directory one position down + + + Move Down + + + + + + + + 0 + 33 + + + + + 0 + 33 + + + + Remove selected directory + + + Remove + + + @@ -127,6 +187,18 @@ + + + 0 + 33 + + + + + 0 + 33 + + Move selected archive one position up @@ -135,8 +207,27 @@ - + + + + <html><head/><body><p><span style=" font-style:italic;">note: archives that are not part of current Content List are </span><span style=" font-style:italic; background-color:#00ff00;">highlighted</span></p></body></html> + + + + + + + 0 + 33 + + + + + 0 + 33 + + Move selected archive one position down @@ -145,13 +236,6 @@ - - - - <html><head/><body><p><span style=" font-style:italic;">note: archives that are not part of current Content List are </span><span style=" font-style:italic; background-color:#00ff00;">highlighted</span></p></body></html> - - - @@ -339,8 +423,7 @@ - - + .. New Content List @@ -355,8 +438,7 @@ - - + .. Clone Content List @@ -374,8 +456,7 @@ - - + .. Delete Content List @@ -403,8 +484,7 @@ - - + .. Refresh Data Files diff --git a/files/ui/importpage.ui b/files/ui/importpage.ui new file mode 100644 index 0000000000..3e2b0c5e64 --- /dev/null +++ b/files/ui/importpage.ui @@ -0,0 +1,146 @@ + + + ImportPage + + + + 0 + 0 + 514 + 397 + + + + Form + + + + + + Morrowind Installation Wizard + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Run &Installation Wizard + + + + + + + + + + Morrowind Settings Importer + + + + + + + + File to import settings from: + + + + + + + + + + Browse... + + + + + + + + + Import add-on and plugin selection (creates a new Content List) + + + true + + + + + + + Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters, +so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar +to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts. + + + Import bitmap fonts setup + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Run &Settings Importer + + + + + + + + + 4 + + + false + + + + + + + + + + Qt::Vertical + + + + + + + + diff --git a/files/ui/mainwindow.ui b/files/ui/mainwindow.ui index 54a2d16a1f..54a369999d 100644 --- a/files/ui/mainwindow.ui +++ b/files/ui/mainwindow.ui @@ -25,32 +25,6 @@ - - - - - 400 - 80 - - - - - 16777215 - 80 - - - - #iconWidget { - background-image: url(":/images/openmw-header.png"); - background-color: palette(base); - background-repeat: no-repeat; - background-attachment: scroll; - background-position: right; -} - - - - @@ -86,7 +60,7 @@ - QDialogButtonBox::Close + QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Ok @@ -94,6 +68,119 @@ + + + toolBar + + + Qt::LeftToRight + + + QToolBar { + background-attachment: fixed; + background-image: url(:/images/openmw-header.png); + background-repeat: none; + background-position: right; + border: 0px; +} + +QToolButton { + min-width: 70px; +} + + + + false + + + + 48 + 48 + + + + Qt::ToolButtonTextUnderIcon + + + false + + + TopToolBarArea + + + false + + + + + + + + + true + + + + :/images/openmw-plugin.png:/images/openmw-plugin.png + + + Data Files + + + Allows to setup data files and directories + + + + + + + + + + + true + + + + :/images/preferences-video.png:/images/preferences-video.png + + + Graphics + + + Allows to change graphics settings + + + + + true + + + + :/images/preferences.png:/images/preferences.png + + + Settings + + + Allows to tweak engine settings + + + + + true + + + + :/images/preferences-advanced.png:/images/preferences-advanced.png + + + Import + + + Allows to import data from original engine + + diff --git a/files/ui/playpage.ui b/files/ui/playpage.ui deleted file mode 100644 index f5537ccd11..0000000000 --- a/files/ui/playpage.ui +++ /dev/null @@ -1,189 +0,0 @@ - - - PlayPage - - - - 0 - 0 - 274 - 317 - - - - - - - #Scroll { - background-image: url(":/images/playpage-background.png"); - background-repeat: no-repeat; - background-position: top; -} - - - - - 30 - - - 100 - - - 30 - - - - - #profilesComboBox { - padding: 1px 18px 1px 3px; - - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:0.2 rgba(0, 0, 0, 25), stop:1 white); - border-width: 1px; - border-color: rgba(0, 0, 0, 125); - border-style: solid; - border-radius: 2px; - - font-size: 12pt; - color: black; -} - -/*QComboBox gets the "on" state when the popup is open */ -#profilesComboBox:!editable:on { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 rgba(0, 0, 0, 75), - stop:0.1 rgba(0, 0, 0, 15), - stop:0.2 rgba(255, 255, 255, 55)); - - border: 1px solid rgba(0, 0, 0, 55); -} - -#profilesComboBox:on { /* shift the text when the popup opens */ - padding-top: 3px; - padding-left: 4px; -} - -#profilesComboBox::drop-down { - subcontrol-origin: padding; - subcontrol-position: top right; - - border-width: 1px; - border-left-width: 1px; - border-left-color: darkgray; - border-left-style: solid; /* just a single line */ - border-top-right-radius: 3px; /* same radius as the QComboBox */ - border-bottom-right-radius: 3px; -} - -#profilesComboBox::down-arrow { - image: url(":/images/down.png"); -} - -#profilesComboBox::down-arrow:on { /* shift the arrow when popup is open */ - top: 1px; - left: 1px; -} - -#profilesComboBox QAbstractItemView { - border: 0px; -} - - - - - - - #profileLabel { - font-size: 18pt; - color: black; -} - - - - Current Content List: - - - - - - - - 200 - 85 - - - - - 200 - 85 - - - - #playButton { - height: 50px; - margin-bottom: 30px; - - background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, - stop:0 rgba(255, 255, 255, 200), - stop:0.1 rgba(255, 255, 255, 15), - stop:0.49 rgba(255, 255, 255, 75), - stop:0.5 rgba(0, 0, 0, 0), - stop:0.9 rgba(0, 0, 0, 55), - stop:1 rgba(0, 0, 0, 100)); - - font-size: 26pt; - color: black; - - border-right: 1px solid rgba(0, 0, 0, 155); - border-left: 1px solid rgba(0, 0, 0, 55); - border-top: 1px solid rgba(0, 0, 0, 55); - border-bottom: 1px solid rgba(0, 0, 0, 155); - - border-radius: 5px; -} - -#playButton:hover { - border-bottom: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); - border-top: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); - border-right: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); - border-left: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0)); - border-width: 2px; - border-style: solid; -} - -#playButton:pressed { - background: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 rgba(0, 0, 0, 75), - stop:0.1 rgba(0, 0, 0, 15), - stop:0.2 rgba(255, 255, 255, 55) - stop:0.95 rgba(255, 255, 255, 55), - stop:1 rgba(255, 255, 255, 155)); - - border: 1px solid rgba(0, 0, 0, 55); -} - - - Play - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - diff --git a/files/ui/settingspage.ui b/files/ui/settingspage.ui index c59f596f5b..c61b4f4229 100644 --- a/files/ui/settingspage.ui +++ b/files/ui/settingspage.ui @@ -6,177 +6,1354 @@ 0 0 - 514 - 397 + 732 + 503 - - Form - - + - - - Morrowind Content Language + + + 0 - - - - - - 250 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Morrowind Installation Wizard - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Run &Installation Wizard - - - - - - - - - - Morrowind Settings Importer - - - - - - - - File to import settings from: + + + Game Mechanics + + + + + + + + <html><head/><body><p>Make disposition change of merchants caused by trading permanent.</p></body></html> + + + Permanent barter disposition changes + + + + + + + <html><head/><body><p>Make player followers and escorters start combat with enemies who have started combat with them or the player. Otherwise they wait for the enemies or the player to do an attack first.</p></body></html> + + + Followers defend immediately + + + + + + + <html><head/><body><p>Make Damage Fatigue magic effect uncapped like Drain Fatigue effect.</p><p>This means that unlike Morrowind you will be able to knock down actors using this effect.</p></body></html> + + + Uncapped Damage Fatigue + + + + + + + <html><head/><body><p>Stops combat with NPCs affected by Calm spells every frame -- like in Morrowind without the MCP.</p></body></html> + + + Classic Calm spells behavior + + + + + + + <html><head/><body><p>Make the value of filled soul gems dependent only on soul magnitude.</p></body></html> + + + Soulgem values rebalance + + + + + + + <html><head/><body><p>Makes player swim a bit upward from the line of sight. Applies only in third person mode. Intended to make simpler swimming without diving.</p></body></html> + + + Swim upward correction + + + + + + + <html><head/><body><p>Make enchanted weaponry without Magical flag bypass normal weapons resistance, like in Morrowind.</p></body></html> + + + Enchanted weapons are magical + + + + + + + <html><head/><body><p>Make stealing items from NPCs that were knocked down possible during combat.</p></body></html> + + + Always allow stealing from knocked out actors + + + + + + + <html><head/><body><p>Effects of reflected Absorb spells are not mirrored -- like in Morrowind.</p></body></html> + + + Classic reflected Absorb spells behavior + + + + + + + <html><head/><body><p>Enable navigator. When enabled background threads are started to build nav mesh for world geometry. Pathfinding system uses nav mesh to build paths. When disabled only pathgrid is used to build paths. Single-core CPU systems may have big performance impact on exiting interior location and moving across exterior world. May slightly affect performance on multi-core CPU systems. Multi-core CPU systems may have different latency for nav mesh update depending on other settings and system performance. Moving across external world, entering/exiting location produce nav mesh update. NPC and creatures may not be able to find path before nav mesh is built around them. Try to disable this if you want to have old fashioned AI which doesn’t know where to go when you stand behind that stone and casting a firebolt.</p></body></html> + + + Build nav mesh for world geometry + + + + + + + <html><head/><body><p>If enabled NPCs apply evasion maneuver to avoid collisions with others.</p></body></html> + + + NPCs avoid collisions + + + + + + + <html><head/><body><p>Don't use race weight in NPC movement speed calculations.</p></body></html> + + + Racial variation in speed fix + + + + + + + <html><head/><body><p>If this setting is true, the player is allowed to loot actors (e.g. summoned creatures) during death animation, if they are not in combat. In this case we have to increment death counter and run disposed actor's script instantly.</p><p>If this setting is false, player has to wait until end of death animation in all cases. Makes using of summoned creatures exploit (looting summoned Dremoras and Golden Saints for expensive weapons) a lot harder. Conflicts with mannequin mods, which use SkipAnim to prevent end of death animation.</p></body></html> + + + Can loot during death animation + + + + + + + Give NPC an ability to swim over the water surface when they follow other actor independently from their ability to swim. Has effect only when nav mesh building is enabled. + + + Always allow NPC to follow over water surface + + + + + + + + + <html><head/><body><p>Makes unarmed creature attacks able to reduce armor condition, just as attacks from NPCs and armed creatures.</p></body></html> + + + Unarmed creature attacks damage armor + + + + + + + <html><head/><body><p>Allow non-standard ammunition solely to bypass normal weapon resistance or weakness.</p></body></html> + + + Only appropriate ammunition bypasses normal weapon resistance + + + + + + + + + Factor strength into hand-to-hand combat: + + + + + + + 0 + + + + Off + + + + + Affect werewolves + + + + + Do not affect werewolves + + + + + + + + <html><head/><body><p>How many threads will be spawned to compute physics update in the background. A value of 0 means that the update will be performed in the main thread.</p><p>A value greater than 1 requires the Bullet library be compiled with multithreading support.</p></body></html> + + + Background physics threads: + + + + + + + + + + Actor collision shape type: + + + + + + + Collision is used for both physics simulation and navigation mesh generation for pathfinding. Cylinder gives the best consistency bewtween available navigation paths and ability to move by them. Changing this value affects navigation mesh generation therefore navigation mesh disk cache generated for one value will not be useful with another. + + + Axis-aligned bounding box + + + + Axis-aligned bounding box + + + + + Rotating box + + + + + Cylinder + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Visuals + + + + + + true + + + + + 0 + 0 + 680 + 882 + + + + + + Animations + + + + + + <html><head/><body><p>Use casting animations for magic items, just as for spells.</p></body></html> + + + Use magic item animation + + + + + + + <html><head/><body><p>Makes NPCs and player movement more smooth. Recommended to use with "turn to movement direction" enabled.</p></body></html> + + + Smooth movement + + + + + + + <html><head/><body><p>Load per-group KF-files and skeleton files from Animations folder</p></body></html> + + + Use additional animation sources + + + + + + + <html><head/><body><p>Affects side and diagonal movement. Enabling this setting makes movement more realistic.</p><p>If disabled then the whole character's body is pointed to the direction of view. Diagonal movement has no special animation and causes sliding.</p><p>If enabled then the character turns lower body to the direction of movement. Upper body is turned partially. Head is always pointed to the direction of view. In combat mode it works only for diagonal movement. In non-combat mode it changes straight right and straight left movement as well. Also turns the whole body up or down when swimming according to the movement direction.</p></body></html> + + + Turn to movement direction + + + + + + + 20 + + + + + false + + + <html><head/><body><p>Render holstered weapons (with quivers and scabbards), requires modded assets.</p></body></html> + + + Weapon sheathing + + + + + + + false + + + <html><head/><body><p>Render holstered shield, requires modded assets.</p></body></html> + + + Shield sheathing + + + + + + + + + + + + Shaders + + + + + + <html><head/><body><p>If this option is enabled, normal maps are automatically recognized and used if they are named appropriately + (see 'normal map pattern', e.g. for a base texture foo.dds, the normal map texture would have to be named foo_n.dds). + If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file (.nif or .osg file). Affects objects.</p></body></html> + + + Auto use object normal maps + + + + + + + <html><head/><body><p>See 'auto use object normal maps'. Affects terrain.</p></body></html> + + + Auto use terrain normal maps + + + + + + + <html><head/><body><p>If this option is enabled, specular maps are automatically recognized and used if they are named appropriately + (see 'specular map pattern', e.g. for a base texture foo.dds, + the specular map texture would have to be named foo_spec.dds). + If this option is disabled, normal maps are only used if they are explicitly listed within the mesh file + (.osg file, not supported in .nif files). Affects objects.</p></body></html> + + + Auto use object specular maps + + + + + + + <html><head/><body><p>If a file with pattern 'terrain specular map pattern' exists, use that file as a 'diffuse specular' map. The texture must contain the layer colour in the RGB channel (as usual), and a specular multiplier in the alpha channel.</p></body></html> + + + Auto use terrain specular maps + + + + + + + <html><head/><body><p>Normally environment map reflections aren't affected by lighting, which makes environment-mapped (and thus bump-mapped objects) glow in the dark. + Morrowind Code Patch includes an option to remedy that by doing environment-mapping before applying lighting, this is the equivalent of that option. + Affected objects will use shaders. + </p></body></html> + + + Bump/reflect map local lighting + + + + + + + <html><head/><body><p>Allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation. Can negatively impact performance.</p></body></html> + + + Use anti-alias alpha testing + + + + + + + <html><head/><body><p>Enables soft particles for particle effects. This technique softens the intersection between individual particles and other opaque geometry by blending between them.</p></body></html> + + + Soft Particles + + + + + + + <html><head/><body><p>Simulate coverage-preserving mipmaps to prevent alpha-tested meshes shrinking as they get further away. Will cause meshes whose textures have coverage-preserving mipmaps to grow, though, so refer to mod installation instructions for how to set this.</p></body></html> + + + Adjust coverage for alpha test + + + + + + + <html><head/><body><p>EXPERIMENTAL: Stop rain and snow from falling through overhangs and roofs.</p></body></html> + + + Weather Particle Occlusion + + + + + + + + + + Fog + + + + + + <html><head/><body><p>By default, the fog becomes thicker proportionally to your distance from the clipping plane set at the clipping distance, which causes distortion at the edges of the screen. + This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.</p></body></html> + + + Radial fog + + + + + + + <html><head/><body><p>Use exponential fog formula. By default, linear fog is used.</p></body></html> + + + Exponential fog + + + + + + + <html><head/><body><p>Reduce visibility of clipping plane by blending objects with the sky.</p></body></html> + + + Sky blending + + + + + + + false + + + <html><head/><body><p>The fraction of the maximum distance at which blending with the sky starts.</p></body></html> + + + Sky blending start + + + + + + + false + + + 3 + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.005000000000000 + + + + + + + + + + Terrain + + + + + + + + Viewing distance + + + + + + + Cells + + + 0.000000000000000 + + + 0.500000000000000 + + + + + + + + + + + <html><head/><body><p>Controls how large an object must be to be visible in the scene. The object’s size is divided by its distance to the camera and the result of the division is compared with this value. The smaller this value is, the more objects you will see in the scene.</p></body></html> + + + Object paging min size + + + + + + + 3 + + + 0.000000000000000 + + + 0.250000000000000 + + + 0.005000000000000 + + + + + + + + + <html><head/><body><p>If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.</p></body></html> + + + Distant land + + + + + + + 20 + + + + + <html><head/><body><p>Use object paging for active cells grid.</p></body></html> + + + Active grid object paging + + + + + + + + + + + + Models + + + + + + <html><head/><body><p>If this setting is true, supporting models will make use of day night switch nodes.</p></body></html> + + + Day night switch nodes + + + + + + + + + + Post Processing + + + + + + <html><head/><body><p>If this setting is true, post processing will be enabled.</p></body></html> + + + Enable post processing + + + + + + + 20 + + + + + false + + + <html><head/><body><p>Re-render transparent objects with forced alpha clipping.</p></body></html> + + + Transparent postpass + + + + + + + + + false + + + <html><head/><body><p>Controls how much eye adaptation can change from frame to frame. Smaller values makes for slower transitions.</p></body></html> + + + Auto exposure speed + + + + + + + false + + + 3 + + + 0.010000000000000 + + + 10.000000000000000 + + + 0.001000000000000 + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + - - - - - - - - Browse... - - - - - - - - - Import add-on and plugin selection (creates a new Content List) - - - true - - - - - - - Import bitmap fonts setup - - - Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters, -so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar -to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts. - - - true - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Run &Settings Importer - - - - - - - - - 4 - - - false - - - - + + + + + + + Audio + + + + + + + + Select your preferred audio device. + + + Audio Device + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Default + + + + + + + + + + + + This setting controls HRTF, which simulates 3D sound on stereo systems. + + + HRTF + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Automatic + + + + + Off + + + + + On + + + + + + + + + + + + Select your preferred HRTF profile. + + + HRTF Profile + + + + + + + + 0 + 0 + + + + + 283 + 0 + + + + 0 + + + + Default + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Interface + + + + + + + + 1 + + + + Off + + + + + Tool Tip Only + + + + + Crosshair Only + + + + + Tool Tip and Crosshair + + + + + + + + 2 + + + 0.500000000000000 + + + 8.000000000000000 + + + 0.250000000000000 + + + 1.000000000000000 + + + + + + + 12 + + + 18 + + + 1 + + + 16 + + + + + + + <html><head/><body><p>This setting scales GUI windows. A value of 1.0 results in the normal scale.</p></body></html> + + + GUI scaling factor + + + + + + + <html><head/><body><p>Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. </p><p>The default value is false.</p></body></html> + + + Show effect duration + + + + + + + <html><head/><body><p>If this setting is true, dialogue topics will have a different color if the topic is specific to the NPC you're talking to or the topic was previously seen. Color can be changed in settings.cfg.</p><p>The default value is false.</p></body></html> + + + Change dialogue topic color + + + + + + + Size of characters in game texts. + + + Font size + + + + + + + <html><head/><body><p>Enable zooming on local and global maps.</p></body></html> + + + Can zoom on maps + + + + + + + <html><head/><body><p>If this setting is true, containers supporting graphic herbalism will do so instead of opening the menu.</p></body></html> + + + Enable graphic herbalism + + + + + + + <html><head/><body><p>If this setting is true, damage bonus of arrows and bolts will be shown on item tooltip.</p><p>The default value is false.</p></body></html> + + + Show projectile damage + + + + + + + <html><head/><body><p>If this setting is true, melee weapons reach and speed will be shown on item tooltip.</p><p>The default value is false.</p></body></html> + + + Show melee info + + + + + + + <html><head/><body><p>Stretch menus, load screens, etc. to the window aspect ratio.</p></body></html> + + + Stretch menu background + + + + + + + Show owned objects + + + + + + + <html><head/><body><p>Whether or not the chance of success will be displayed in the enchanting menu.</p><p>The default value is false.</p></body></html> + + + Show enchant chance + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Bug Fixes + + + + + + <html><head/><body><p>Prevents merchants from equipping items that are sold to them.</p></body></html> + + + Merchant equipping fix + + + + + + + <html><head/><body><p>Trainers now only choose which skills to train using their base skill points, allowing mercantile improving effects to be used without making mercantile an offered skill.</p></body></html> + + + Trainers choose their training skills based on their base skill points + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Miscellaneous + + + + + + Saves + + + + + + <html><head/><body><p>This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.</p></body></html> + + + Add "Time Played" to saves + + + + + + + + + Maximum Quicksaves + + + + + + + 1 + + + + + + + + + + + + Other + + + + + + + + Screenshot Format + + + + + + + + JPG + + + + + PNG + + + + + TGA + + + + + + + + + + Notify on saved screenshot + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + Testing + + + + + + These settings are intended for testing mods and will cause issues if used for normal gameplay. + + + true + + + + + + + Qt::Horizontal + + + + + + + <html><head/><body><p>OpenMW will capture control of the cursor if this setting is true.</p><p>In “look mode”, OpenMW will center the cursor regardless of the value of this setting (since the cursor/crosshair is always centered in the OpenMW window). However, in GUI mode, this setting determines the behavior when the cursor is moved outside the OpenMW window. If true, the cursor movement stops at the edge of the window preventing access to other applications. If false, the cursor is allowed to move freely on the desktop.</p><p>This setting does not apply to the screen where escape has been pressed, where the cursor is never captured. Regardless of this setting “Alt-Tab” or some other operating system dependent key sequence can be used to allow the operating system to regain control of the mouse cursor. This setting interacts with the minimize on focus loss setting by affecting what counts as a focus loss. Specifically on a two-screen configuration it may be more convenient to access the second screen with setting disabled.</p><p>Note for developers: it’s desirable to have this setting disabled when running the game in a debugger, to prevent the mouse cursor from becoming unusable when the game pauses on a breakpoint.</p></body></html> + + + Grab cursor + + + + + + + Skip menu and generate default character + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 0 + 0 + + + + + + + + Start default character at + + + + + + + default cell + + + + + + + + + Run script after startup: + + + + + + + + + + + + Browse… + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + - - - - Qt::Vertical - - - - 20 - 40 - - - -