From 523ea046a9f68846be0e28321ce38d94553d33ec Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Mon, 25 Sep 2023 17:27:45 -0500 Subject: [PATCH] Tutorial updates - Add ability to exit tutorial early - Update and clarify many of the tutorials --- src/mainwindow.cpp | 14 +- src/mainwindow.ui | 99 ++++++------ src/pluginlistview.cpp | 2 +- src/tutorials/TutorialCanceller.qml | 55 +++++++ src/tutorials/TutorialOverlay.qml | 9 ++ .../tutorial_conflictresolution_main.js | 144 +++++++++++------- .../tutorial_conflictresolution_modinfo.js | 8 +- src/tutorials/tutorial_firststeps_main.js | 71 +++++---- src/tutorials/tutorial_firststeps_modinfo.js | 5 +- src/tutorials/tutorial_firststeps_settings.js | 8 +- src/tutorials/tutorial_primer_main.js | 1 + src/tutorials/tutorials.js | 47 +++--- 12 files changed, 289 insertions(+), 174 deletions(-) create mode 100644 src/tutorials/TutorialCanceller.qml diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ebe7da98d..8c5110e0a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -371,8 +371,8 @@ MainWindow::MainWindow(Settings& settings, OrganizerCore& organizerCore, if (organizerCore.managedGame()->sortMechanism() == MOBase::IPluginGame::SortMechanism::NONE) { - ui->bossButton->setDisabled(true); - ui->bossButton->setToolTip(tr("There is no supported sort mechanism for this game. " + ui->sortButton->setDisabled(true); + ui->sortButton->setToolTip(tr("There is no supported sort mechanism for this game. " "You will probably have to use a third-party tool.")); } @@ -2265,14 +2265,8 @@ void MainWindow::tutorialTriggered() { QAction* tutorialAction = qobject_cast(sender()); if (tutorialAction != nullptr) { - if (QMessageBox::question(this, tr("Start Tutorial?"), - tr("You're about to start a tutorial. For technical " - "reasons it's not possible to end " - "the tutorial early. Continue?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - TutorialManager::instance().activateTutorial("MainWindow", - tutorialAction->data().toString()); - } + TutorialManager::instance().activateTutorial("MainWindow", + tutorialAction->data().toString()); } } diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 0c4d9984f..c2ce37b32 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -203,9 +203,11 @@ - Filter: only show the separators that match the current filters -Show: always show separators -Hide: never show separators + + Filter: only show the separators that match the current filters + Show: always show separators + Hide: never show separators + @@ -253,12 +255,14 @@ Hide: never show separators Pick a module collection - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Create profiles here. Each profile contains its own list of active mods and esps. This way you can quickly switch between setups for different playthroughs.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Please note that right now your esp load order is not kept separate for different profiles.</span></p></body></html> + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> + p, li { white-space: pre-wrap; } + </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> + <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Create profiles here. Each profile contains its own list of active mods and esps. This way you can quickly switch between setups for different playthroughs.</span></p> + <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Please note that right now your esp load order is not kept separate for different profiles.</span></p></body></html> + @@ -434,7 +438,7 @@ p, li { white-space: pre-wrap; } false - 35 + 39 true @@ -614,7 +618,6 @@ p, li { white-space: pre-wrap; } 9 - 75 true @@ -622,12 +625,14 @@ p, li { white-space: pre-wrap; } Pick a program to run. - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You can add new Tools to this list, but I can't promise tools I haven't tested will work.</span></p></body></html> + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> + p, li { white-space: pre-wrap; } + </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> + <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.</span></p> + <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You can add new Tools to this list, but I can't promise tools I haven't tested will work.</span></p></body></html> + @@ -665,7 +670,6 @@ p, li { white-space: pre-wrap; } 10 - 75 true @@ -673,11 +677,13 @@ p, li { white-space: pre-wrap; } Run program - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Run the selected program with ModOrganizer enabled.</span></p></body></html> + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> + p, li { white-space: pre-wrap; } + </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> + <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Run the selected program with ModOrganizer enabled.</span></p></body></html> + @@ -727,11 +733,13 @@ p, li { white-space: pre-wrap; } Create a shortcut in your start menu or on the desktop to the specified program - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This creates a start menu shortcut that directly starts the selected program with the MO active.</span></p></body></html> + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> + p, li { white-space: pre-wrap; } + </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> + <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This creates a start menu shortcut that directly starts the selected program with the MO active.</span></p></body></html> + Shortcut @@ -799,7 +807,7 @@ p, li { white-space: pre-wrap; } - + Sort the plugins using LOOT. @@ -1038,10 +1046,12 @@ p, li { white-space: pre-wrap; } List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order. - BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they "compete" with loose files in your data directory over which is loaded. - By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored! + + BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they "compete" with loose files in your data directory over which is loaded. + By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored! - BSAs checked here are loaded in such a way that your installation order is obeyed properly. + BSAs checked here are loaded in such a way that your installation order is obeyed properly. + false @@ -1207,13 +1217,15 @@ p, li { white-space: pre-wrap; } - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This is a list of all save games for this game. Hover over a list entry to get detailed information about the save including a list of esps/esms that were used at the time this save was created but aren't active now.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + <html><head><meta name="qrichtext" content="1" /><style type="text/css"> + p, li { white-space: pre-wrap; } + </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> + <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This is a list of all save games for this game. Hover over a list entry to get detailed information about the save including a list of esps/esms that were used at the time this save was created but aren't active now.</span></p> + <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> + <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> + QAbstractScrollArea::AdjustIgnored @@ -1467,7 +1479,7 @@ p, li { white-space: pre-wrap; } 0 0 1009 - 21 + 22 @@ -1540,13 +1552,10 @@ p, li { white-space: pre-wrap; } - 80 - 42 + 82 + 44 - - QDockWidget::AllDockWidgetFeatures - Log diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index a0a0162fb..3c2bf0002 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -206,7 +206,7 @@ void PluginListView::setup(OrganizerCore& core, MainWindow* mw, Ui::MainWindow* }); // sort - connect(mwui->bossButton, &QPushButton::clicked, [=] { + connect(mwui->sortButton, &QPushButton::clicked, [=] { onSortButtonClicked(); }); diff --git a/src/tutorials/TutorialCanceller.qml b/src/tutorials/TutorialCanceller.qml new file mode 100644 index 000000000..b2e88fae4 --- /dev/null +++ b/src/tutorials/TutorialCanceller.qml @@ -0,0 +1,55 @@ +import QtQuick 2.7 + +// rectangle for description texts +Rectangle { + property alias text: textBox.text + property alias cancelVisible: cancelIcon.visible + property int innerWidth; + signal clicked + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 70 + width: textBox.width + 30 + height: textBox.height + 8 + border.color: "black" + border.width: 3 + smooth: true + //opacity: 0.9 + z: 10000 + color: "#FF707070" + + Image { + id: cancelIcon + anchors.bottom: parent.bottom + anchors.bottomMargin: 5 + anchors.right: parent.right + anchors.rightMargin: 5 + source: "qrc:/MO/gui/multiply_red" + + SequentialAnimation on opacity { + loops: Animation.Infinite + + PauseAnimation { duration: 500 } + PropertyAnimation { easing.type: Easing.InOutSine; duration: 400; to: 0.0 } + PropertyAnimation { easing.type: Easing.OutInSine; duration: 400; to: 1.0 } + } + } + + Text { + id: textBox + text: "Exit Tutorial" + font.pointSize: 12 + font.bold: false + width: innerWidth + font.family: "Courier" + wrapMode: Text.WordWrap + anchors.centerIn: parent + } + + MouseArea { + id: clickArea + anchors.fill: parent + onClicked: parent.clicked() + } +} diff --git a/src/tutorials/TutorialOverlay.qml b/src/tutorials/TutorialOverlay.qml index 8c2a36e79..e937195cc 100644 --- a/src/tutorials/TutorialOverlay.qml +++ b/src/tutorials/TutorialOverlay.qml @@ -46,6 +46,15 @@ Rectangle { } } + TutorialCanceller { + id: tutorialCanceller + innerWidth: 200 + anchors.bottomMargin: offsetBottom - 30 + onClicked: { + Logic.cancelTutorial() + } + } + Rectangle { id: disabledBackground anchors.fill: parent diff --git a/src/tutorials/tutorial_conflictresolution_main.js b/src/tutorials/tutorial_conflictresolution_main.js index 46518a937..09a946ba8 100644 --- a/src/tutorials/tutorial_conflictresolution_main.js +++ b/src/tutorials/tutorial_conflictresolution_main.js @@ -2,44 +2,56 @@ function getTutorialSteps() { return [ function() { - tutorial.text = qsTr("There are multiple types of conflicts you may encounter when dealing with Mods. " - +"This tutorial will try to cover and explain how to deal with all of them.") + tutorial.text = qsTr("Welcome to the conflict resolution tutorial. This tutorial is very dense with " + + "information, so take it slow and feel free to revisit as needed - until you have " + + "a solid grasp of the types of conflicts and tools to manage them.") + waitForClick() + }, + function() { + tutorial.text = qsTr("Some info primarily applies to Bethesda Game Studios games. If this does not apply " + + "to you, you can exit the tutorial when you reach the section about \"record conflicts\".") + waitForClick() + }, + function() { + tutorial.text = qsTr("There are multiple types of conflicts you may encounter when dealing with mods. " + + "This tutorial will try to cover and explain how to deal with all of them.") waitForClick() }, function() { tutorial.text = qsTr("First up are file conflicts. These occur when two mods contain the same file. " - +"Most commonly this happens when several mods replace the same standard asset from " - +"the game, like the texture of an armor.") + + "Most commonly this happens when several mods replace the same standard asset from " + + "the game, like the texture of an armor.") waitForClick() }, function() { - tutorial.text = qsTr("As an example, say you install \"ModA\" which contains stylish new iron and leather armor. " - +"Then you install \"ModB\" which contains sexy ebony and leather armor. Obviously there is a " - +"conflict now: which leather armor to use?") + tutorial.text = qsTr("As an example, say you install \"Mod A\" which contains stylish new iron and leather armor. " + + "Then you install \"Mod B\" which contains sexy ebony and leather armor. Obviously there is a " + + "conflict now: which leather armor to use?") waitForClick() }, function() { - tutorial.text = qsTr("If you were to install the mods manually, when installing \"ModB\" you would be asked if you want " - +"to overwrite conflicting files. If you choose yes, you get the leather armor from \"ModB\" otherwise " - +"you keep the one from \"ModA\". If you later decide you made the wrong choice, " + tutorial.text = qsTr("If you were to install the mods manually, when installing \"Mod B\" you would be asked if you want " + +"to overwrite conflicting files. If you choose yes, you get the leather armor from \"Mod B\" otherwise " + +"you keep the one from \"Mod A\". If you later decide you made the wrong choice, " +"you have to reinstall one of the mods.") waitForClick() }, function() { - tutorial.text = qsTr("With MO, both ModA and ModB get installed completely (no overwrite dialog) and by default " - +"\"ModB\" gets to provide the leather armor because it's automatically assigned the higher priority.") + tutorial.text = qsTr("With MO, both \"Mod A\" and \"Mod B\" are installed independently, without overwriting " + + "any files. Initially, \"Mod B\" gets to provide the leather armor because it's automatically " + + "assigned the higher priority.") waitForClick() }, function() { - tutorial.text = qsTr("However, you can change the mod priority at any time using drag&drop on this list. " - +"If you assign \"ModA\" a higher priority, it provides the leather armor, no re-installation required. " - +"Since the priorities of mods in this list are treated as if the mods were installed in that order, " - +"I tend to talk about \"installation order\".") + tutorial.text = qsTr("However, you can change the mod priority at any time by dragging and dropping mods in the list. " + + "If you assign \"Mod A\" a higher priority, it provides the leather armor, no re-installation required. " + + "Since the priorities of mods in this list are treated as if the mods were installed in that order, " + + "this can generally be referred to as \"installation order\".") highlightItem("modList", false) waitForClick() }, function() { - tutorial.text = qsTr("If the \"Flags\"-column is enabled in the mod list, it will show you which mods are involved in a conflict and how...") + tutorial.text = qsTr("If the \"Flags\" column is enabled in the mod list, it will show you which mods are involved in a conflict.") waitForClick() }, function() { @@ -52,11 +64,11 @@ function getTutorialSteps() { waitForClick() }, function() { - tutorial.text = qsTr(" indicates that both of the above is true.") + tutorial.text = qsTr(" indicates that both of the these are true.") waitForClick() }, function() { - tutorial.text = qsTr(" indicates that the mod is completely overwritten by another. You could as well disable it."); + tutorial.text = qsTr(" indicates that the mod is completely overwritten. It provides no active files and is essentially disabled."); waitForClick() }, function() { @@ -64,7 +76,7 @@ function getTutorialSteps() { waitForClick() }, function() { - tutorial.text = qsTr("Option A: Switch to the \"Data\"-tab if necessary") + tutorial.text = qsTr("Option A: Switch to the \"Data\" tab.") if (!tutorialControl.waitForTabOpen("tabWidget", "dataTab")) { highlightItem("tabWidget", false) waitForClick() @@ -73,90 +85,112 @@ function getTutorialSteps() { } }, function() { - tutorial.text = qsTr("... here, if you mark the highlighted control, the tree will only display files in conflict. " - +"In the right column, it says which mod currently provides the mod (because it has highest priority) " - +"and if you hover your mouse over that info, it will list which other mods contains it.") + tutorial.text = qsTr("In the \"Data\" tab, if you check the highlighted control, the tree will only show " + + "conflicted files. In the right column, it displays which mod is currently providing " + + "the file (due to having the highest priority), and if you hover your mouse over that " + + "info, it will list which other mods contain it.") highlightItem("dataTabShowOnlyConflicts", false) waitForClick() }, function() { - tutorial.text = qsTr("Option B: Open the Information-Dialog of an enabled mod you're interested in by " - +"either double-clicking it or selecting Information... from the right-click menu") + tutorial.text = qsTr("Option B: Open the Information dialog of an enabled mod by either " + + "double-clicking it or selecting Information... from the right-click menu.") highlightItem("modList", true) manager.activateTutorial("ModInfoDialog", "tutorial_conflictresolution_modinfo.js") applicationWindow.modInfoDisplayed.connect(nextStep) }, function() { unhighlight() - tutorial.text = qsTr("This was everything to know about file conflicts. The second type of conflict we have to deal with " - +"are \"record conflicts\".") + tutorial.text = qsTr("That is everything you need to know about file conflicts. The second type of conflict " + + "to deal with is \"record conflicts\".") waitForClick() }, function() { - tutorial.text = qsTr("I told you in the \"First Steps\" tutorial how the esp/esm/esl plugins contain changes to the game world " - +"like modifications to the terrain or existing NPCs. Each change like this is stored in a record, hence the " - +"name \"record conflict\". For example when two mods try to change the same location, only one change can become active.") + tutorial.text = qsTr("In the \"First Steps\" tutorial, you learned how plugins contain changes to the game " + + "world, like modifications to the terrain or existing NPCs. Each of these changes is " + + "stored in a record, hence the name \"record conflict\". For example, when two mods " + + "try to change the same location, only one change can become active.") waitForClick() }, function() { - tutorial.text = qsTr("As with file conflicts you can't really fix these conflicts, you have to choose which change you want. " - +"This time around however, if you choose wrong, your game may become unstable because there may be " - +"dependencies between the records of a mod.") + tutorial.text = qsTr("Similar to the mod list, you will have to choose which plugins will be loaded last " + + "and take priority over other records. This time around however, choosing an incorrect " + + "order can cause your game to become unstable, as there may be strict dependencies " + + "between plugin files and records.") waitForClick() }, function() { - tutorial.text = qsTr("Please open the \"Plugins\"-tab...") + tutorial.text = qsTr("Please open the \"Plugins\" tab...") highlightItem("tabWidget", true) if (!tutorialControl.waitForTabOpen("tabWidget", "espTab")) { nextStep() } }, function() { - tutorial.text = qsTr("Again you can use drag&drop to change priorities of plugins, thus deciding which plugin takes " - +"precedence in regards to conflicts. This is commonly called the \"load order\". " - +"But how do you know how to order the plugins?") + tutorial.text = qsTr("As with mods, you can drag and drop plugins to change their priority, thus deciding " + + "which plugins take precedence in regards to conflicts. This is commonly called the " + + "\"load order\". But how do you know how to order the plugins?") waitForClick() }, function() { unhighlight() - tutorial.text = qsTr("Unlike with file conflicts, MO does not provide help on finding conflicts. The good news is, there " - +"already is a perfect tool for that called LOOT. LOOT is available on the Nexus and integrates " - +"neatly with MO. Basically, if you don't have LOOT yet, install it once this tutorial is over.") + tutorial.text = qsTr("Unlike with file conflicts, MO can provide only minimal help, indicating whether " + + "required \"master\" plugins are present in the load order. The good news is, there " + + "is a perfect tool for that called LOOT.") waitForClick() }, function() { - tutorial.text = qsTr("After you installed LOOT in the default location (follow its instructions), start MO again and LOOT should automatically appear as an Executable...") + tutorial.text = qsTr("MO has a built in integration with LOOT which can be used via the \"Sort\" button for " + + "any supported game. This will attempt to use any configuration set up within the " + + "main LOOT application.") + highlightItem("sortButton", false) + waitForClick() + }, + function() { + unhighlight() + tutorial.text = qsTr("If LOOT has been installed, Mod Organizer should detect it for any supported game and " + + "automatically add it to the available tools.") highlightItem("startGroup", false) waitForClick() }, function() { - tutorial.text = qsTr("When you run LOOT, it will automatically re-organize plugins for best compatibility (overwriting your manual changes). " - +"It will also open a report in your browser that warns about incompatibilities. You should read the report, at least " - +"for new mods.") + tutorial.text = qsTr("When you run LOOT, it will automatically re-organize plugins for best compatibility " + + "(overwriting your manual changes). It will also notate the plugin list with " + + "information about patches, incompatibilities, and other useful info. This is true " + + "in both the main LOOT application and for the integration within Mod Organizer.") waitForClick() }, function() { unhighlight() - tutorial.text = qsTr("The final type of conflicts are also \"record conflicts\". Like the previous type. It's confusing, so " - +"I'll just call them \"lists conflicts\" instead. The difference is the types of " - +"records in conflict. The ones in question here can be merged so you may be able to get all modifications in your game.") + tutorial.text = qsTr("The final type of conflicts are a subset of \"record conflicts\". We will call these " + + "\"lists conflicts\". As briefly mentioned earlier, these types of record conflicts " + + "can be merged so you may be able to get all modifications in your game.") + waitForClick() + }, + function() { + tutorial.text = qsTr("One common example of such records are leveled lists that contain all the items that " + + "may spawn at a specific character level. Traditionally, if multiple mods add items to " + + "such a list, only one of these mods will actually take effect. In some cases, there " + + "are community-made patches to resolve these issues.") waitForClick() }, function() { - tutorial.text = qsTr("One common example of such records are leveled lists that contain all the items that may spawn at a specific " - +"character level. Traditionally, if multiple mods add items to such a list, only one is in effect...") + tutorial.text = qsTr("Fortunately, there are also tools to merge many types of records so that they can all " + + "take effect. For Oblivion, Skyrim, and Fallout 4, look for Wrye Bash. For Fallout 3 " + + "and New Vegas, you can use Wrye Flash. These can create a \"bashed patch,\" which is " + + "a plugin that combines many mergeable records from all of your mods. There are other, " + + "similar tools for more specific tasks such as 'Synergy'.") waitForClick() }, function() { - tutorial.text = qsTr("... but there are tools to merge those mods so you can have the effects of all of them. Again, this " - +"functionality is not integrated with MO because there are already great tools. For Oblivion and Skyrim " - +"look for wrye bash, for fallout 3/nv it's wrye flash. For Skyrim there is also " - +"\"SkyBash\". All of these can create a so-called \"bashed patch\" which is a plugin that contains the combined " - +"mergeable records from all your mods.") + tutorial.text = qsTr("Finally, advanced users may consider using 'xEdit', a robust tool for comparing, " + + "modifying, and cleaning plugin records. With this you can create your own custom " + + "patch files to merge any combination of plugins and records.") waitForClick() }, function() { - tutorial.text = qsTr("This completes the tutorial.") + tutorial.text = qsTr("This completes the tutorial. Hopefully you have a better grasp on the intricacies of " + + "conflict resolution. Good luck, and happy modding!") waitForClick() } diff --git a/src/tutorials/tutorial_conflictresolution_modinfo.js b/src/tutorials/tutorial_conflictresolution_modinfo.js index 04691ef6d..98ca660d8 100644 --- a/src/tutorials/tutorial_conflictresolution_modinfo.js +++ b/src/tutorials/tutorial_conflictresolution_modinfo.js @@ -1,4 +1,5 @@ function getTutorialSteps() { + tutorialCanceller.visible = false return [ function() { tutorial.text = qsTr("Please switch to the \"Conflicts\"-Tab.") @@ -8,12 +9,13 @@ function getTutorialSteps() { } }, function() { - tutorial.text = qsTr("Here you can see a list of files in this mod that out-prioritize others " - +"in a file conflict and one with files where this mod is overridden.") + tutorial.text = qsTr("Here you can see two lists: a list of files that this mod overwrites that are also " + + "provided by other mods, and a list of files in this mod which are overwritten by " + + "one or more other mods.") waitForClick() }, function() { - tutorial.text = qsTr("Please close the information dialog again.") + tutorial.text = qsTr("Please close the information dialog.") waitForClick() } diff --git a/src/tutorials/tutorial_firststeps_main.js b/src/tutorials/tutorial_firststeps_main.js index 76faf9730..0837de4c2 100644 --- a/src/tutorials/tutorial_firststeps_main.js +++ b/src/tutorials/tutorial_firststeps_main.js @@ -3,20 +3,25 @@ function getTutorialSteps() { return [ function() { - tutorial.text = qsTr("Welcome to the ModOrganizer Tutorial! This will guide you through the most common features of MO." - + "\nPlease go along with the tutorial because some things can't be demonstrated if you skip something.") + tutorial.text = qsTr("Welcome to the Mod Organizer Tutorial! This will guide you through the most common " + + "features of MO2.\n\n" + + "It is highly recommended for first-time users to complete the tutorial from beginning " + + "to end to properly demonstrate key components of the tool.") waitForClick() }, function() { - tutorial.text = qsTr("Before we continue with the step-by-step tutorial, I'd like to tell you about the other ways you can receive help on ModOrganizer.") + tutorial.text = qsTr("Before we continue with the step-by-step tutorial, here are a few ways you can receive " + + "help with Mod Organizer.") waitForClick() }, function() { - tutorial.text = qsTr("The highlighted button provides hints on solving potential problems MO recognized automatically.") + tutorial.text = qsTr("The highlighted button will display potential problems detected with your setup and may " + + "suggest solutions. (Click it and then close the window to proceed.)") if (tutorialControl.waitForAction("actionNotifications")) { - tutorial.text += qsTr("\nThere IS a notification now but you may want to hold off on clearing it until after completing the tutorial.") + tutorial.text += qsTr("\n\nIt appears you have one now, however you can hold off on clearing it until after " + + "completing the tutorial.") highlightAction("actionNotifications", true) } else { highlightAction("actionNotifications", false) @@ -26,7 +31,8 @@ function getTutorialSteps() function() { console.log("next") - tutorial.text = qsTr("This button provides multiple sources of information and further tutorials.") + tutorial.text = qsTr("This button contains additional information about the application, links to other sources " + + "of help, and further tutorials. (Open the menu to proceed.)") if (tutorialControl.waitForAction("actionHelp")) { highlightAction("actionHelp", true) } else { @@ -37,21 +43,23 @@ function getTutorialSteps() function() { unhighlight() - tutorial.text = qsTr("Finally there are tooltips on almost every part of Mod Organizer. If there is a control " - + "in MO you don't understand, please try hovering over it to get a short description or " - + "use \"Help on UI\" from the help menu to get a longer explanation") + tutorial.text = qsTr("Finally, there are tooltips and extra information available all across Mod Organizer. If " + + "there is a control you don't understand, please try hovering over it for a short " + + "description. Alternatively, you can use \"Help on UI\" from the Help menu to click on " + + "some controls and get a comprehensive explanation.") waitForClick() }, function() { - tutorial.text = qsTr("This list displays all mods installed through MO. It also displays installed DLCs and some mods " - + "installed outside MO but you have limited control over those in MO.") + tutorial.text = qsTr("This list displays all mods installed through MO2. It also displays installed DLCs and " + + "any 'unmanaged' mods installed outside MO2. You have limited control over those.") highlightItem("modList", false) waitForClick() }, function() { - tutorial.text = qsTr("Before we start installing mods, let's have a quick look at the settings.") + tutorial.text = qsTr("Before we start installing mods, let's have a quick look at the settings. (Open the " + + "settings dialog to proceed via the highlighted button.)") manager.activateTutorial("SettingsDialog", "tutorial_firststeps_settings.js") if (tutorialControl.waitForAction("actionSettings")) { highlightAction("actionSettings", true) @@ -63,13 +71,13 @@ function getTutorialSteps() function() { unhighlight() - tutorial.text = qsTr("Now it's time to install a few mods!" - + "Please go along with this because we need a few mods installed to demonstrate other features") + tutorial.text = qsTr("Now it's time to install a mod!\n\n" + + "(This will be a requirement in order to demonstrate other features later.)") waitForClick() }, function() { - tutorial.text = qsTr("There are a few ways to get mods into ModOrganizer. " + tutorial.text = qsTr("There are a few ways to get mods into Mod Organizer. " + "If you associated MO with NXM links in the settings you can now use your regular browser to send downloads from Nexus to MO. " + "Click on \"Nexus\" to open nexus, find a mod and click the green download buttons on Nexus saying \"Download with Manager\".") if (tutorialControl.waitForAction("actionNexus") && @@ -89,7 +97,8 @@ function getTutorialSteps() }, function() { - tutorial.text = qsTr("Downloads will appear on the \"Downloads\"-tab here. You have to download and install at least one mod to proceed.") + tutorial.text = qsTr("Downloads will appear on the \"Downloads\" tab here. You have to download and install at " + + "least one mod to proceed.") organizer.modInstalled.connect(nextStep) highlightItem("tabWidget", true) }, @@ -97,18 +106,19 @@ function getTutorialSteps() function() { unhighlight() organizer.modInstalled.disconnect(nextStep) - tutorial.text = qsTr("Great, you just installed your first mod. Please note that the installation procedure may differ based on how a mod was packaged.") + tutorial.text = qsTr("Great, you just installed your first mod. Please note that the installation procedure " + + "may differ based on how a mod was packaged.") waitForClick() }, function() { unhighlight() - tutorial.text = qsTr("Now you know all about downloading and installing mods but they are not enabled yet...") + tutorial.text = qsTr("Now you know all about downloading and installing mods, but they are not enabled yet...") waitForClick() }, function() { - tutorial.text = qsTr("Install a few more mods if you want, then enable mods by checking them in the left pane. " + tutorial.text = qsTr("Install a few more mods if you want, then enable them by checking them in the left pane. " + "Mods that aren't enabled have no effect on the game whatsoever. ") highlightItem("modList", true) modList.tutorialModlistUpdate.connect(nextStep) @@ -122,9 +132,9 @@ function getTutorialSteps() }, function() { - tutorial.text = qsTr("...but most contain plugins. These are plugins for the game and are required " - +"to add stuff to the game (new weapons, armors, quests, areas, ...). " - +"Please open the \"Plugins\"-tab to get a list of plugins.") + tutorial.text = qsTr("...but for some games they may contain plugins. These are files the game must load and " + + "are required to change or add aspects of the game (new weapons, armors, quests, areas, ...).\n\n" + + "Please open the \"Plugins\" tab to get a list of plugins.") if (tutorialControl.waitForTabOpen("tabWidget", "espTab")) { highlightItem("tabWidget", true) } else { @@ -133,13 +143,13 @@ function getTutorialSteps() }, function() { - tutorial.text = qsTr("You will notice some plugins are grayed out. These are part of the main game and can't be " + tutorial.text = qsTr("You may notice some plugins are grayed out. These are part of the main game and can't be " +"disabled.") waitForClick() }, function() { - tutorial.text = qsTr("A single mod may contain zero, one or multiple esps. Some or all may be optional. " + tutorial.text = qsTr("A single mod may contain zero, one, or multiple plugin files. Some or all may be optional. " + "If in doubt, please consult the documentation of the individual mod. " + "To do so, right-click the mod and select \"Information\".") highlightItem("modList", true) @@ -148,7 +158,7 @@ function getTutorialSteps() }, function() { - tutorial.text = qsTr("Now you know how to download, install and enable mods.\n" + tutorial.text = qsTr("Now you know how to download, install, and enable mods.\n\n" + "It's important you always start the game from inside MO, otherwise " + "the mods you installed here won't work.") highlightItem("startGroup", false) @@ -156,16 +166,17 @@ function getTutorialSteps() }, function() { - tutorial.text = qsTr("This combobox lets you choose what to start. This way you can start the game, Launcher, " - + "Script Extender, the Creation Kit or other tools. If you miss a tool you can also configure this list " - + "but that is an advanced topic.") + tutorial.text = qsTr("This combobox lets you choose what to start. This is how you will launch the game " + + "or any other tool which must access the game's mod directories. If a tool is not " + + "listed here, you can also configure these options, but that is an advanced topic.") highlightItem("executablesListBox", false) waitForClick() }, function() { - tutorial.text = qsTr("This completes the basic tutorial. As homework go play a bit! After you " - + "have installed more mods you may want to read the tutorial on conflict resolution.") + tutorial.text = qsTr("This completes the basic tutorial. Feel free to play the game and try out your new mods! " + + "Once you have installed a larger number, you may want to continue with the tutorial " + + "on conflict resolution.") highlightItem("startButton", false) waitForClick() } diff --git a/src/tutorials/tutorial_firststeps_modinfo.js b/src/tutorials/tutorial_firststeps_modinfo.js index 1ed0dab7e..5bb1163df 100644 --- a/src/tutorials/tutorial_firststeps_modinfo.js +++ b/src/tutorials/tutorial_firststeps_modinfo.js @@ -1,9 +1,10 @@ function getTutorialSteps() { + tutorialCanceller.visible = false return [ function() { tutorial.text = qsTr("This dialog tries to expose as much information about a mod as possible. " - +"Depending on the mod this may include readmes, screenshots, optional plugins and so on. " + +"Depending on the mod, this may include readmes, screenshots, optional plugins and so on. " +"If a certain type of information was not found in a mod, the corresponding tab " +"is grayed out.") highlightItem("tabWidget", false) @@ -11,7 +12,7 @@ function getTutorialSteps() }, function() { tutorial.text = qsTr("If you installed the mod from Nexus, the corresponding tab should give you direct " - +"access to the mod page.") + +"access to the mod page description, which can be refreshed directly from Nexus.") waitForClick() }, function() { diff --git a/src/tutorials/tutorial_firststeps_settings.js b/src/tutorials/tutorial_firststeps_settings.js index b0e0c3c30..94ca26a3a 100644 --- a/src/tutorials/tutorial_firststeps_settings.js +++ b/src/tutorials/tutorial_firststeps_settings.js @@ -1,16 +1,18 @@ function getTutorialSteps() { + tutorialCanceller.visible = false return [ function() { highlightItem("tabWidget", true) - tutorial.text = qsTr("You can use your regular browser to download from Nexus.\nPlease open the \"Nexus\"-tab") + tutorial.text = qsTr("It is possible to download files directly from Nexus.\n\n" + + "Please open the \"Nexus\" tab.") tutorialControl.waitForTabOpen("tabWidget", "nexusTab") }, function() { highlightItem("associateButton", false) - tutorial.text = qsTr("Click this button so that \"DOWNLOAD WITH MANAGER\"-buttons " - +"are download with Mod Organizer.") + tutorial.text = qsTr("Clicking on this button should register Nexus \"Download with Manager\" buttons " + +"to download with Mod Organizer.") waitForClick() }, diff --git a/src/tutorials/tutorial_primer_main.js b/src/tutorials/tutorial_primer_main.js index 640364cbf..b74c83fa3 100644 --- a/src/tutorials/tutorial_primer_main.js +++ b/src/tutorials/tutorial_primer_main.js @@ -126,6 +126,7 @@ function setupTooptips() { } function getTutorialSteps() { + tutorialCanceller.visible = false return [ function() { tutorial.text = qsTr("Click to quit") diff --git a/src/tutorials/tutorials.js b/src/tutorials/tutorials.js index cdb16d598..a0348420d 100644 --- a/src/tutorials/tutorials.js +++ b/src/tutorials/tutorials.js @@ -31,51 +31,48 @@ function highlightAction(actionName, click) { highlight.visible = true } -function unhighlight() -{ +function unhighlight() { highlight.visible = false } -function waitForClick() -{ +function waitForClick() { waitingForClick = true; description.continueVisible = true // ui needs to be locked, otherwise the tutorial-view does not receive mouse-events! tutorialControl.lockUI(true) } -function clickNext() -{ +function cancelTutorial() { + tutorialControl.finish() +} + +function clickNext() { if (waitingForClick) { nextStep() } } -function nextStep() -{ - waitingForClick = false; - description.continueVisible = false - if (step < tutorialSteps.length) { - tutorialControl.lockUI(false) - step++ - tutorialSteps[step - 1]() - } else { - tutorialControl.finish() - } +function nextStep() { + waitingForClick = false; + description.continueVisible = false + if (step < tutorialSteps.length) { + tutorialControl.lockUI(false) + step++ + tutorialSteps[step - 1]() + } else { + tutorialControl.finish() + } } -function sameStep() -{ - tutorialSteps[step - 1]() +function sameStep() { + tutorialSteps[step - 1]() } -function onTabChanged(func) -{ - tutToplevel.tabChanged.connect(func) +function onTabChanged(func) { + tutToplevel.tabChanged.connect(func) } -function init() -{ +function init() { var res = Qt.include("file:///" + scriptName) if (res.status !== 0) { console.log("failed to load " + scriptName + ": " + res.status)