diff --git a/api/logic/modplatform/atlauncher/ATLPackManifest.cpp b/api/logic/modplatform/atlauncher/ATLPackManifest.cpp index 149cb9c1..f28fd35c 100644 --- a/api/logic/modplatform/atlauncher/ATLPackManifest.cpp +++ b/api/logic/modplatform/atlauncher/ATLPackManifest.cpp @@ -147,7 +147,20 @@ static void loadVersionMod(ATLauncher::VersionMod & p, QJsonObject & obj) { p.optional = Json::ensureBoolean(obj, QString("optional"), false); p.recommended = Json::ensureBoolean(obj, QString("recommended"), false); p.selected = Json::ensureBoolean(obj, QString("selected"), false); + p.hidden = Json::ensureBoolean(obj, QString("hidden"), false); + p.library = Json::ensureBoolean(obj, QString("library"), false); + p.group = Json::ensureString(obj, QString("group"), ""); + if(obj.contains("depends")) { + auto dependsArr = Json::requireArray(obj, "depends"); + for (const auto depends : dependsArr) { + p.depends.append(Json::requireString(depends)); + } + } + p.client = Json::ensureBoolean(obj, QString("client"), false); + + // computed + p.effectively_hidden = p.hidden || p.library; } void ATLauncher::loadVersion(PackVersion & v, QJsonObject & obj) diff --git a/api/logic/modplatform/atlauncher/ATLPackManifest.h b/api/logic/modplatform/atlauncher/ATLPackManifest.h index 48e1d344..376587b0 100644 --- a/api/logic/modplatform/atlauncher/ATLPackManifest.h +++ b/api/logic/modplatform/atlauncher/ATLPackManifest.h @@ -90,7 +90,15 @@ struct VersionMod bool optional; bool recommended; bool selected; + bool hidden; + bool library; + QString group; + QVector depends; + bool client; + + // computed + bool effectively_hidden; }; struct PackVersion diff --git a/application/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp b/application/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp index 129d9fed..cffe5d55 100644 --- a/application/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp +++ b/application/pages/modplatform/atlauncher/AtlOptionalModDialog.cpp @@ -4,8 +4,16 @@ AtlOptionalModListModel::AtlOptionalModListModel(QWidget *parent, QVector mods) : QAbstractListModel(parent), m_mods(mods) { - for (const auto& mod : mods) { - m_selection[mod.name] = mod.selected; + // fill mod index + for (int i = 0; i < m_mods.size(); i++) { + auto mod = m_mods.at(i); + m_index[mod.name] = i; + } + // set initial state + for (int i = 0; i < m_mods.size(); i++) { + auto mod = m_mods.at(i); + m_selection[mod.name] = false; + setMod(mod, i, mod.selected, false); } } @@ -61,11 +69,7 @@ bool AtlOptionalModListModel::setData(const QModelIndex &index, const QVariant & auto row = index.row(); auto mod = m_mods.at(row); - // toggle the state - m_selection[mod.name] = !m_selection[mod.name]; - - emit dataChanged(AtlOptionalModListModel::index(index.row(), EnabledColumn), - AtlOptionalModListModel::index(index.row(), EnabledColumn)); + toggleMod(mod, row); return true; } @@ -113,6 +117,71 @@ void AtlOptionalModListModel::clearAll() { AtlOptionalModListModel::index(m_mods.size() - 1, EnabledColumn)); } +void AtlOptionalModListModel::toggleMod(ATLauncher::VersionMod mod, int index) { + setMod(mod, index, !m_selection[mod.name]); +} + +void AtlOptionalModListModel::setMod(ATLauncher::VersionMod mod, int index, bool enable, bool shouldEmit) { + if (m_selection[mod.name] == enable) return; + + m_selection[mod.name] = enable; + + // disable other mods in the group, if applicable + if (enable && !mod.group.isEmpty()) { + for (int i = 0; i < m_mods.size(); i++) { + if (index == i) continue; + auto other = m_mods.at(i); + + if (mod.group == other.group) { + setMod(other, i, false, shouldEmit); + } + } + } + + for (const auto& dependencyName : mod.depends) { + auto dependencyIndex = m_index[dependencyName]; + auto dependencyMod = m_mods.at(dependencyIndex); + + // enable/disable dependencies + if (enable) { + setMod(dependencyMod, dependencyIndex, true, shouldEmit); + } + + // if the dependency is 'effectively hidden', then track which mods + // depend on it - so we can efficiently disable it when no more dependents + // depend on it. + auto dependants = m_dependants[dependencyName]; + + if (enable) { + dependants.append(mod.name); + } + else { + dependants.removeAll(mod.name); + + // if there are no longer any dependents, let's disable the mod + if (dependencyMod.effectively_hidden && dependants.isEmpty()) { + setMod(dependencyMod, dependencyIndex, false, shouldEmit); + } + } + } + + // disable mods that depend on this one, if disabling + if (!enable) { + auto dependants = m_dependants[mod.name]; + for (const auto& dependencyName : dependants) { + auto dependencyIndex = m_index[dependencyName]; + auto dependencyMod = m_mods.at(dependencyIndex); + + setMod(dependencyMod, dependencyIndex, false, shouldEmit); + } + } + + if (shouldEmit) { + emit dataChanged(AtlOptionalModListModel::index(index, EnabledColumn), + AtlOptionalModListModel::index(index, EnabledColumn)); + } +} + AtlOptionalModDialog::AtlOptionalModDialog(QWidget *parent, QVector mods) : QDialog(parent), ui(new Ui::AtlOptionalModDialog) { diff --git a/application/pages/modplatform/atlauncher/AtlOptionalModDialog.h b/application/pages/modplatform/atlauncher/AtlOptionalModDialog.h index 8b0dbdb6..a1df43f6 100644 --- a/application/pages/modplatform/atlauncher/AtlOptionalModDialog.h +++ b/application/pages/modplatform/atlauncher/AtlOptionalModDialog.h @@ -37,9 +37,15 @@ public slots: void selectRecommended(); void clearAll(); +private: + void toggleMod(ATLauncher::VersionMod mod, int index); + void setMod(ATLauncher::VersionMod mod, int index, bool enable, bool shouldEmit = true); + private: QVector m_mods; QMap m_selection; + QMap m_index; + QMap> m_dependants; }; class AtlOptionalModDialog : public QDialog {