aboutsummaryrefslogtreecommitdiff
path: root/libs/game_bethesda/src/games/starfield/gamestarfield.cpp
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-16 19:51:40 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-05-16 19:51:40 -0500
commit549b84b0f8815737c93913b200dde3821a062cb0 (patch)
treef6bfd96729016847438beda9416c93f447838077 /libs/game_bethesda/src/games/starfield/gamestarfield.cpp
parenta0747cabb420d699be3a1dd4c63f47c5153bd7b9 (diff)
mo2: catch up to upstream 2.5.3 Betas 3-11
Multi-area sync against the upstream MO2 2.5.3 beta line. Roughly three logical sections share the diff: Catch-up (Betas 3-11): - nxmaccessmanager: drop "supporter" from validRoles so non-premium supporters get the right download path (Beta 10) - mainwindow: disable tutorial triggers at the trigger sites; Qt 6.11 lockup on child windows (Beta 4) - basic_games: add Kingdom Come Deliverance 2, Slay the Spire 2 (Betas 11, 6) - uibase: add NXM collections parsing fields/accessors + (?:nxm|modl) scheme support in NXMUrl (Beta 9) - Starfield: blueprintPrefix() on IPluginGame; plugins.txt write suppression for blueprint plugins; hasInvalidBlueprint / hasUnpairedBlueprint diagnoses; Title-keyed content catalog consolidation (Betas 3-5) - downloadmanager + systemtraymanager + organizercore + iuserinterface + settings: add Beta 11 "show notifications when downloads complete or fail" toggle and the showNotification plumbing - nxmaccessmanager: restore upstream re-entry guard for OAuth refresh and the styled OAuth response page (Beta 11) - organizer_en.ts and game-bethesda *_en.ts: pull upstream HEAD strings Workarounds tab removed: - Delete settingsdialogworkarounds.{h,cpp}; drop the tab in settingsdialog.ui; move "Enable archives parsing" into General - Hardcode GameSettings::forceEnableCoreFiles() to true so the primary-master toggle-off bug (DLCs unchecked on tab refresh) can't recur; setter becomes a no-op - No-op stubs for offlineMode, useProxy, useCustomBrowser, Steam appID/credentials, executablesBlacklist, skipFileSuffixes, skipDirectories — UI gone, accessors keep call sites green - settingsdialognexus drops the custom-browser wiring; spawn drops the "Change the blacklist" Retry path Beta 9 download manager port: - fileID-first match in nxmFilesAvailable and nxmFileInfoFromMd5Available; filename remains the fallback - NexusInterface::isActiveFileStatus() helper; rewrite of nxmUpdatesAvailable to use pickNewestVersion / findUpdateChainSuccessors / resolveInstalledFileId. ARCHIVED_HIDDEN now correctly treated as inactive - std::optional<unsigned int> reservedID on DownloadInfo factories and on the three addDownload overloads; startDownloadURLs and startDownloadURLWithMeta return the canonical DownloadID instead of m_ActiveDownloads.size()-1 (which was the index, not an ID) - QHash<unsigned int, DownloadInfo*> m_ByID maintained alongside m_ActiveDownloads at every mutation site; downloadInfoByID is now O(1) via m_ByID.value(id, nullptr) LOOT remains intentionally stripped (lootcli build cruft cleaned up separately). BSA wide-path skipped — Linux UTF-8 paths already work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'libs/game_bethesda/src/games/starfield/gamestarfield.cpp')
-rw-r--r--libs/game_bethesda/src/games/starfield/gamestarfield.cpp66
1 files changed, 63 insertions, 3 deletions
diff --git a/libs/game_bethesda/src/games/starfield/gamestarfield.cpp b/libs/game_bethesda/src/games/starfield/gamestarfield.cpp
index ebec948..06a6d79 100644
--- a/libs/game_bethesda/src/games/starfield/gamestarfield.cpp
+++ b/libs/game_bethesda/src/games/starfield/gamestarfield.cpp
@@ -22,6 +22,7 @@
#include <QDir>
#include <QFileInfo>
#include <QList>
+#include <QRegularExpression>
#include <QObject>
#include <QString>
#include <QStringList>
@@ -337,15 +338,23 @@ QStringList GameStarfield::CCPlugins() const
// as Creations. The StarfieldUnmanagedMods class handles parsing mod names and files.
if (unmanagedMods.get()) {
auto contentCatalog = unmanagedMods->parseContentCatalog();
- for (const auto& mod : contentCatalog) {
- if (!plugins.contains(mod.first, Qt::CaseInsensitive)) {
- plugins.append(mod.first);
+ for (auto& mod : contentCatalog) {
+ QStringList pluginFiles = mod.second.files.filter(QRegularExpression(
+ "\\.es(m|p|l)$", QRegularExpression::CaseInsensitiveOption));
+ if (!pluginFiles.isEmpty()) {
+ plugins += pluginFiles;
}
}
}
+ plugins.removeDuplicates();
return plugins;
}
+QString GameStarfield::blueprintPrefix() const
+{
+ return "blueprintships-";
+}
+
IPluginGame::SortMechanism GameStarfield::sortMechanism() const
{
return IPluginGame::SortMechanism::LOOT;
@@ -380,6 +389,10 @@ std::vector<unsigned int> GameStarfield::activeProblems() const
if (testFilePresent())
result.push_back(PROBLEM_TEST_FILE);
}
+ if (hasInvalidBlueprint())
+ result.push_back(PROBLEM_INVALID_BLUEPRINT);
+ if (hasUnpairedBlueprint())
+ result.push_back(PROBLEM_UNPAIRED_BLUEPRINT);
}
return result;
}
@@ -412,6 +425,38 @@ bool GameStarfield::testFilePresent() const
return false;
}
+bool GameStarfield::hasInvalidBlueprint() const
+{
+ auto plugins = m_Organizer->pluginList()->pluginNames();
+ for (auto plugin : plugins) {
+ if (m_Organizer->pluginList()->isBlueprintFlagged(plugin)) {
+ if (!plugin.startsWith(blueprintPrefix(), Qt::CaseInsensitive) ||
+ !m_Organizer->pluginList()->hasMasterExtension(plugin))
+ return true;
+ } else if (plugin.startsWith(blueprintPrefix(), Qt::CaseInsensitive)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GameStarfield::hasUnpairedBlueprint() const
+{
+ auto plugins = m_Organizer->pluginList()->pluginNames();
+ for (auto plugin : plugins) {
+ if (plugin.startsWith(blueprintPrefix(), Qt::CaseInsensitive) &&
+ m_Organizer->pluginList()->hasMasterExtension(plugin)) {
+ QString parent = plugin.mid(blueprintPrefix().size(),
+ plugin.size() - blueprintPrefix().size() - 4);
+ auto mainPlugin = plugins.filter(QRegularExpression(
+ "^" + parent + "\\.es(m|p|l)$", QRegularExpression::CaseInsensitiveOption));
+ if (mainPlugin.isEmpty())
+ return true;
+ }
+ }
+ return false;
+}
+
QString GameStarfield::shortDescription(unsigned int key) const
{
switch (key) {
@@ -419,6 +464,10 @@ QString GameStarfield::shortDescription(unsigned int key) const
return tr("You have active ESP plugins in Starfield");
case PROBLEM_TEST_FILE:
return tr("sTestFile entries are present");
+ case PROBLEM_INVALID_BLUEPRINT:
+ return tr("Invalid blueprint plugins found");
+ case PROBLEM_UNPAIRED_BLUEPRINT:
+ return tr("Unpaired blueprint plugins found");
}
return "";
}
@@ -446,6 +495,17 @@ QString GameStarfield::fullDescription(unsigned int key) const
"settings in your StarfieldCustom.ini. These must be removed or the game "
"will not read the plugins.txt file. Management is still disabled.</p>");
}
+ case PROBLEM_INVALID_BLUEPRINT:
+ return tr(
+ "<p>You have a blueprint file that is invalid. Blueprints require the "
+ "blueprint flag and prefix and must have the ESM extension to be valid.</p>");
+ case PROBLEM_UNPAIRED_BLUEPRINT:
+ return tr(
+ "<p>You have a valid blueprint file that has no paired main plugin. The only "
+ "way to load blueprint files is by enabling a main plugin with the same base "
+ "name. This is the part after the prefix and before the extension.</p><p>eg. "
+ "<strong>BlueprintShips-example.esm</strong> should have a paired main plugin "
+ "<strong>example.esm</strong></p>");
}
return "";
}