Lots of collection progress.

This commit is contained in:
Ottermandias 2023-04-11 11:28:56 +02:00
parent d908f22a17
commit 3f33bab296
22 changed files with 666 additions and 636 deletions

View file

@ -31,8 +31,6 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public (int, int) ApiVersion
=> (4, 19);
private readonly Dictionary<ModCollection, ModCollection.ModSettingChangeDelegate> _delegates = new();
public event Action<string>? PreSettingsPanelDraw;
public event Action<string>? PostSettingsPanelDraw;
@ -110,10 +108,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
private CollectionResolver _collectionResolver;
private CutsceneService _cutsceneService;
private ModImportManager _modImportManager;
private CollectionEditor _collectionEditor;
public unsafe PenumbraApi(CommunicatorService communicator, Penumbra penumbra, ModManager modManager, ResourceLoader resourceLoader,
Configuration config, CollectionManager collectionManager, DalamudServices dalamud, TempCollectionManager tempCollections,
TempModManager tempMods, ActorService actors, CollectionResolver collectionResolver, CutsceneService cutsceneService, ModImportManager modImportManager)
TempModManager tempMods, ActorService actors, CollectionResolver collectionResolver, CutsceneService cutsceneService, ModImportManager modImportManager, CollectionEditor collectionEditor)
{
_communicator = communicator;
_penumbra = penumbra;
@ -127,17 +126,16 @@ public class PenumbraApi : IDisposable, IPenumbraApi
_actors = actors;
_collectionResolver = collectionResolver;
_cutsceneService = cutsceneService;
_modImportManager = modImportManager;
_modImportManager = modImportManager;
_collectionEditor = collectionEditor;
_lumina = (Lumina.GameData?)_dalamud.GameData.GetType()
.GetField("gameData", BindingFlags.Instance | BindingFlags.NonPublic)
?.GetValue(_dalamud.GameData);
foreach (var collection in _collectionManager.Storage)
SubscribeToCollection(collection);
_communicator.CollectionChange.Subscribe(SubscribeToNewCollections);
_resourceLoader.ResourceLoaded += OnResourceLoaded;
_communicator.ModPathChanged.Subscribe(ModPathChangeSubscriber);
_communicator.ModPathChanged.Subscribe(ModPathChangeSubscriber);
_communicator.ModSettingChanged.Subscribe(OnModSettingChange, -1000);
}
public unsafe void Dispose()
@ -145,15 +143,9 @@ public class PenumbraApi : IDisposable, IPenumbraApi
if (!Valid)
return;
foreach (var collection in _collectionManager.Storage)
{
if (_delegates.TryGetValue(collection, out var del))
collection.ModSettingChanged -= del;
}
_resourceLoader.ResourceLoaded -= OnResourceLoaded;
_communicator.CollectionChange.Unsubscribe(SubscribeToNewCollections);
_communicator.ModPathChanged.Unsubscribe(ModPathChangeSubscriber);
_communicator.ModPathChanged.Unsubscribe(ModPathChangeSubscriber);
_communicator.ModSettingChanged.Unsubscribe(OnModSettingChange);
_lumina = null;
_communicator = null!;
_penumbra = null!;
@ -167,6 +159,8 @@ public class PenumbraApi : IDisposable, IPenumbraApi
_actors = null!;
_collectionResolver = null!;
_cutsceneService = null!;
_modImportManager = null!;
_collectionEditor = null!;
}
public event ChangedItemClick? ChangedItemClicked;
@ -702,7 +696,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return PenumbraApiEc.ModMissing;
return collection.SetModInheritance(mod.Index, inherit) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
return _collectionEditor.SetModInheritance(collection, mod, inherit) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
}
public PenumbraApiEc TrySetMod(string collectionName, string modDirectory, string modName, bool enabled)
@ -714,7 +708,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
return PenumbraApiEc.ModMissing;
return collection.SetModState(mod.Index, enabled) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
return _collectionEditor.SetModState(collection, mod, enabled) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
}
public PenumbraApiEc TrySetModPriority(string collectionName, string modDirectory, string modName, int priority)
@ -726,7 +720,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
return PenumbraApiEc.ModMissing;
return collection.SetModPriority(mod.Index, priority) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
return _collectionEditor.SetModPriority(collection, mod, priority) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
}
public PenumbraApiEc TrySetModSetting(string collectionName, string modDirectory, string modName, string optionGroupName,
@ -749,7 +743,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
var setting = mod.Groups[groupIdx].Type == GroupType.Multi ? 1u << optionIdx : (uint)optionIdx;
return collection.SetModSetting(mod.Index, groupIdx, setting) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
return _collectionEditor.SetModSetting(collection, mod, groupIdx, setting) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
}
public PenumbraApiEc TrySetModSettings(string collectionName, string modDirectory, string modName, string optionGroupName,
@ -789,7 +783,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
}
}
return collection.SetModSetting(mod.Index, groupIdx, setting) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
return _collectionEditor.SetModSetting(collection, mod, groupIdx, setting) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
}
@ -797,17 +791,13 @@ public class PenumbraApi : IDisposable, IPenumbraApi
{
CheckInitialized();
var sourceModIdx = _modManager
.FirstOrDefault(m => string.Equals(m.ModPath.Name, modDirectoryFrom, StringComparison.OrdinalIgnoreCase))?.Index
?? -1;
var targetModIdx = _modManager
.FirstOrDefault(m => string.Equals(m.ModPath.Name, modDirectoryTo, StringComparison.OrdinalIgnoreCase))?.Index
?? -1;
var sourceMod = _modManager.FirstOrDefault(m => string.Equals(m.ModPath.Name, modDirectoryFrom, StringComparison.OrdinalIgnoreCase));
var targetMod = _modManager.FirstOrDefault(m => string.Equals(m.ModPath.Name, modDirectoryTo, StringComparison.OrdinalIgnoreCase));
if (string.IsNullOrEmpty(collectionName))
foreach (var collection in _collectionManager.Storage)
collection.CopyModSettings(sourceModIdx, modDirectoryFrom, targetModIdx, modDirectoryTo);
_collectionEditor.CopyModSettings(collection, sourceMod, modDirectoryFrom, targetMod, modDirectoryTo);
else if (_collectionManager.Storage.ByName(collectionName, out var collection))
collection.CopyModSettings(sourceModIdx, modDirectoryFrom, targetModIdx, modDirectoryTo);
_collectionEditor.CopyModSettings(collection, sourceMod, modDirectoryFrom, targetMod, modDirectoryTo);
else
return PenumbraApiEc.CollectionMissing;
@ -1127,29 +1117,6 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return true;
}
private void SubscribeToCollection(ModCollection c)
{
var name = c.Name;
void Del(ModSettingChange type, int idx, int _, int _2, bool inherited)
=> ModSettingChanged?.Invoke(type, name, idx >= 0 ? _modManager[idx].ModPath.Name : string.Empty, inherited);
_delegates[c] = Del;
c.ModSettingChanged += Del;
}
private void SubscribeToNewCollections(CollectionType type, ModCollection? oldCollection, ModCollection? newCollection, string _)
{
if (type != CollectionType.Inactive)
return;
if (oldCollection != null && _delegates.TryGetValue(oldCollection, out var del))
oldCollection.ModSettingChanged -= del;
if (newCollection != null)
SubscribeToCollection(newCollection);
}
public void InvokePreSettingsPanel(string modDirectory)
=> PreSettingsPanelDraw?.Invoke(modDirectory);
@ -1166,4 +1133,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
var b = ByteString.FromStringUnsafe(name, false);
return _actors.AwaitedService.CreatePlayer(b, worldId);
}
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int _1, int _2, bool inherited)
=> ModSettingChanged?.Invoke(type, collection.Name, mod?.ModPath.Name ?? string.Empty, inherited);
}

View file

@ -6,6 +6,8 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.Interop.Services;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.Services;
@ -16,6 +18,7 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
{
private readonly ActiveCollections _active;
private readonly CommunicatorService _communicator;
private readonly CharacterUtility _characterUtility;
private readonly Dictionary<ModCollection, ModCollectionCache> _cache = new();
@ -46,17 +49,23 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
public IEnumerable<ModCollection> Active
=> _cache.Keys.Where(c => c.Index > ModCollection.Empty.Index);
public CollectionCacheManager(ActiveCollections active, CommunicatorService communicator)
public CollectionCacheManager(ActiveCollections active, CommunicatorService communicator, CharacterUtility characterUtility)
{
_active = active;
_communicator = communicator;
_active = active;
_communicator = communicator;
_characterUtility = characterUtility;
_communicator.CollectionChange.Subscribe(OnCollectionChange);
_communicator.ModPathChanged.Subscribe(OnModChangeAddition, -100);
_communicator.ModPathChanged.Subscribe(OnModChangeRemoval, 100);
_communicator.TemporaryGlobalModChange.Subscribe(OnGlobalModChange);
_communicator.ModOptionChanged.Subscribe(OnModOptionChange, -100);
_communicator.ModSettingChanged.Subscribe(OnModSettingChange);
_communicator.CollectionInheritanceChanged.Subscribe(OnCollectionInheritanceChange);
CreateNecessaryCaches();
if (!_characterUtility.Ready)
_characterUtility.LoadingFinished += IncrementCounters;
}
public void Dispose()
@ -66,6 +75,9 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
_communicator.ModPathChanged.Unsubscribe(OnModChangeRemoval);
_communicator.TemporaryGlobalModChange.Unsubscribe(OnGlobalModChange);
_communicator.ModOptionChanged.Unsubscribe(OnModOptionChange);
_communicator.ModSettingChanged.Unsubscribe(OnModSettingChange);
_communicator.CollectionInheritanceChanged.Unsubscribe(OnCollectionInheritanceChange);
_characterUtility.LoadingFinished -= IncrementCounters;
}
/// <summary>
@ -170,4 +182,57 @@ public class CollectionCacheManager : IDisposable, IReadOnlyDictionary<ModCollec
collection._cache!.AddMod(mod, true);
}
}
/// <summary> Increment the counter to ensure new files are loaded after applying meta changes. </summary>
private void IncrementCounters()
{
foreach (var (collection, _) in _cache)
++collection.ChangeCounter;
_characterUtility.LoadingFinished -= IncrementCounters;
}
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool _)
{
if (collection._cache == null)
return;
switch (type)
{
case ModSettingChange.Inheritance:
collection._cache.ReloadMod(mod!, true);
break;
case ModSettingChange.EnableState:
if (oldValue == 0)
collection._cache.AddMod(mod!, true);
else if (oldValue == 1)
collection._cache.RemoveMod(mod!, true);
else if (collection[mod!.Index].Settings?.Enabled == true)
collection._cache.ReloadMod(mod!, true);
else
collection._cache.RemoveMod(mod!, true);
break;
case ModSettingChange.Priority:
if (collection._cache.Conflicts(mod!).Count > 0)
collection._cache.ReloadMod(mod!, true);
break;
case ModSettingChange.Setting:
if (collection[mod!.Index].Settings?.Enabled == true)
collection._cache.ReloadMod(mod!, true);
break;
case ModSettingChange.MultiInheritance:
case ModSettingChange.MultiEnableState:
collection._cache.FullRecalculation(collection == _active.Default);
break;
}
}
/// <summary>
/// Inheritance changes are too big to check for relevance,
/// just recompute everything.
/// </summary>
private void OnCollectionInheritanceChange(ModCollection collection, bool _)
=> collection._cache?.FullRecalculation(collection == _active.Default);
}

View file

@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using OtterGui;
using Penumbra.Api.Enums;
using Penumbra.Mods;
using Penumbra.Services;
using Penumbra.Util;
namespace Penumbra.Collections.Manager;
public class CollectionEditor
{
private readonly CommunicatorService _communicator;
private readonly SaveService _saveService;
public CollectionEditor(SaveService saveService, CommunicatorService communicator)
{
_saveService = saveService;
_communicator = communicator;
}
/// <summary> Enable or disable the mod inheritance of mod idx. </summary>
public bool SetModInheritance(ModCollection collection, Mod mod, bool inherit)
{
if (!FixInheritance(collection, mod, inherit))
return false;
InvokeChange(collection, ModSettingChange.Inheritance, mod, inherit ? 0 : 1, 0);
return true;
}
/// <summary>
/// Set the enabled state mod idx to newValue if it differs from the current enabled state.
/// If the mod is currently inherited, stop the inheritance.
/// </summary>
public bool SetModState(ModCollection collection, Mod mod, bool newValue)
{
var oldValue = collection._settings[mod.Index]?.Enabled ?? collection[mod.Index].Settings?.Enabled ?? false;
if (newValue == oldValue)
return false;
var inheritance = FixInheritance(collection, mod, false);
collection._settings[mod.Index]!.Enabled = newValue;
InvokeChange(collection, ModSettingChange.EnableState, mod, inheritance ? -1 : newValue ? 0 : 1, 0);
return true;
}
/// <summary> Enable or disable the mod inheritance of every mod in mods. </summary>
public void SetMultipleModInheritances(ModCollection collection, IEnumerable<Mod> mods, bool inherit)
{
if (!mods.Aggregate(false, (current, mod) => current | FixInheritance(collection, mod, inherit)))
return;
InvokeChange(collection, ModSettingChange.MultiInheritance, null, -1, 0);
}
/// <summary>
/// Set the enabled state of every mod in mods to the new value.
/// If the mod is currently inherited, stop the inheritance.
/// </summary>
public void SetMultipleModStates(ModCollection collection, IEnumerable<Mod> mods, bool newValue)
{
var changes = false;
foreach (var mod in mods)
{
var oldValue = collection._settings[mod.Index]?.Enabled;
if (newValue == oldValue)
continue;
FixInheritance(collection, mod, false);
collection._settings[mod.Index]!.Enabled = newValue;
changes = true;
}
if (!changes)
return;
InvokeChange(collection, ModSettingChange.MultiEnableState, null, -1, 0);
}
/// <summary>
/// Set the priority of mod idx to newValue if it differs from the current priority.
/// If the mod is currently inherited, stop the inheritance.
/// </summary>
public bool SetModPriority(ModCollection collection, Mod mod, int newValue)
{
var oldValue = collection._settings[mod.Index]?.Priority ?? collection[mod.Index].Settings?.Priority ?? 0;
if (newValue == oldValue)
return false;
var inheritance = FixInheritance(collection, mod, false);
collection._settings[mod.Index]!.Priority = newValue;
InvokeChange(collection, ModSettingChange.Priority, mod, inheritance ? -1 : oldValue, 0);
return true;
}
/// <summary>
/// Set a given setting group settingName of mod idx to newValue if it differs from the current value and fix it if necessary.
/// /// If the mod is currently inherited, stop the inheritance.
/// </summary>
public bool SetModSetting(ModCollection collection, Mod mod, int groupIdx, uint newValue)
{
var settings = collection._settings[mod.Index] != null
? collection._settings[mod.Index]!.Settings
: collection[mod.Index].Settings?.Settings;
var oldValue = settings?[groupIdx] ?? mod.Groups[groupIdx].DefaultSettings;
if (oldValue == newValue)
return false;
var inheritance = FixInheritance(collection, mod, false);
collection._settings[mod.Index]!.SetValue(mod, groupIdx, newValue);
InvokeChange(collection, ModSettingChange.Setting, mod, inheritance ? -1 : (int)oldValue, groupIdx);
return true;
}
/// <summary> Copy the settings of an existing (sourceMod != null) or stored (sourceName) mod to another mod, if they exist. </summary>
public bool CopyModSettings(ModCollection collection, Mod? sourceMod, string sourceName, Mod? targetMod, string targetName)
{
if (targetName.Length == 0 && targetMod == null || sourceName.Length == 0)
return false;
// If the source mod exists, convert its settings to saved settings or null if its inheriting.
// If it does not exist, check unused settings.
// If it does not exist and has no unused settings, also use null.
ModSettings.SavedSettings? savedSettings = sourceMod != null
? collection._settings[sourceMod.Index] != null
? new ModSettings.SavedSettings(collection._settings[sourceMod.Index]!, sourceMod)
: null
: collection._unusedSettings.TryGetValue(sourceName, out var s)
? s
: null;
if (targetMod != null)
{
if (savedSettings != null)
{
// The target mod exists and the source settings are not inheriting, convert and fix the settings and copy them.
// This triggers multiple events.
savedSettings.Value.ToSettings(targetMod, out var settings);
SetModState(collection, targetMod, settings.Enabled);
SetModPriority(collection, targetMod, settings.Priority);
foreach (var (value, index) in settings.Settings.WithIndex())
SetModSetting(collection, targetMod, index, value);
}
else
{
// The target mod exists, but the source is inheriting, set the target to inheriting.
// This triggers events.
SetModInheritance(collection, targetMod, true);
}
}
else
{
// The target mod does not exist.
// Either copy the unused source settings directly if they are not inheriting,
// or remove any unused settings for the target if they are inheriting.
if (savedSettings != null)
collection._unusedSettings[targetName] = savedSettings.Value;
else
collection._unusedSettings.Remove(targetName);
}
return true;
}
/// <summary>
/// Change one of the available mod settings for mod idx discerned by type.
/// If type == Setting, settingName should be a valid setting for that mod, otherwise it will be ignored.
/// The setting will also be automatically fixed if it is invalid for that setting group.
/// For boolean parameters, newValue == 0 will be treated as false and != 0 as true.
/// </summary>
public bool ChangeModSetting(ModCollection collection, ModSettingChange type, Mod mod, int newValue, int groupIdx)
{
return type switch
{
ModSettingChange.Inheritance => SetModInheritance(collection, mod, newValue != 0),
ModSettingChange.EnableState => SetModState(collection, mod, newValue != 0),
ModSettingChange.Priority => SetModPriority(collection, mod, newValue),
ModSettingChange.Setting => SetModSetting(collection, mod, groupIdx, (uint)newValue),
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
};
}
/// <summary>
/// Set inheritance of a mod without saving,
/// to be used as an intermediary.
/// </summary>
private static bool FixInheritance(ModCollection collection, Mod mod, bool inherit)
{
var settings = collection._settings[mod.Index];
if (inherit == (settings == null))
return false;
collection._settings[mod.Index] = inherit ? null : collection[mod.Index].Settings?.DeepCopy() ?? ModSettings.DefaultSettings(mod);
return true;
}
/// <summary> Queue saves and trigger changes for any non-inherited change in a collection, then trigger changes for all inheritors. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void InvokeChange(ModCollection changedCollection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx)
{
_saveService.QueueSave(changedCollection);
_communicator.ModSettingChanged.Invoke(changedCollection, type, mod, oldValue, groupIdx, false);
RecurseInheritors(changedCollection, type, mod, oldValue, groupIdx);
}
/// <summary> Trigger changes in all inherited collections. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void RecurseInheritors(ModCollection directParent, ModSettingChange type, Mod? mod, int oldValue, int groupIdx)
{
foreach (var directInheritor in directParent.DirectParentOf)
{
switch (type)
{
case ModSettingChange.MultiInheritance:
case ModSettingChange.MultiEnableState:
_communicator.ModSettingChanged.Invoke(directInheritor, type, null, oldValue, groupIdx, true);
break;
default:
if (directInheritor._settings[mod!.Index] == null)
_communicator.ModSettingChanged.Invoke(directInheritor, type, mod, oldValue, groupIdx, true);
break;
}
RecurseInheritors(directInheritor, type, mod, oldValue, groupIdx);
}
}
}

View file

@ -7,14 +7,16 @@ public class CollectionManager
public readonly InheritanceManager Inheritances;
public readonly CollectionCacheManager Caches;
public readonly TempCollectionManager Temp;
public readonly CollectionEditor Editor;
public CollectionManager(CollectionStorage storage, ActiveCollections active, InheritanceManager inheritances,
CollectionCacheManager caches, TempCollectionManager temp)
CollectionCacheManager caches, TempCollectionManager temp, CollectionEditor editor)
{
Storage = storage;
Active = active;
Inheritances = inheritances;
Caches = caches;
Temp = temp;
Editor = editor;
}
}

View file

@ -1,12 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Interface.Internal.Notifications;
using OtterGui;
using OtterGui.Filesystem;
using Penumbra.Services;
using Penumbra.Util;
namespace Penumbra.Collections.Manager;
/// <summary>
/// ModCollections can inherit from an arbitrary number of other collections.
/// This is transitive, so a collection A inheriting from B also inherits from everything B inherits.
/// Circular dependencies are resolved by distinctness.
/// </summary>
public class InheritanceManager : IDisposable
{
public enum ValidInheritance
{
Valid,
/// <summary> Can not inherit from self </summary>
Self,
/// <summary> Can not inherit from the empty collection </summary>
Empty,
/// <summary> Already inherited from </summary>
Contained,
/// <summary> Inheritance would lead to a circle. </summary>
Circle,
}
private readonly CollectionStorage _storage;
private readonly CommunicatorService _communicator;
private readonly SaveService _saveService;
@ -26,22 +48,88 @@ public class InheritanceManager : IDisposable
_communicator.CollectionChange.Unsubscribe(OnCollectionChange);
}
/// <summary> Check whether a collection can be inherited from. </summary>
public static ValidInheritance CheckValidInheritance(ModCollection potentialInheritor, ModCollection? potentialParent)
{
if (potentialParent == null || ReferenceEquals(potentialParent, ModCollection.Empty))
return ValidInheritance.Empty;
if (ReferenceEquals(potentialParent, potentialInheritor))
return ValidInheritance.Self;
if (potentialInheritor.DirectlyInheritsFrom.Contains(potentialParent))
return ValidInheritance.Contained;
if (ModCollection.InheritedCollections(potentialParent).Any(c => ReferenceEquals(c, potentialInheritor)))
return ValidInheritance.Circle;
return ValidInheritance.Valid;
}
/// <summary>
/// Add a new collection to the inheritance list.
/// We do not check if this collection would be visited before,
/// only that it is unique in the list itself.
/// </summary>
public bool AddInheritance(ModCollection inheritor, ModCollection parent)
=> AddInheritance(inheritor, parent, true);
/// <summary> Remove an existing inheritance from a collection. </summary>
public void RemoveInheritance(ModCollection inheritor, int idx)
{
var parent = inheritor.DirectlyInheritsFrom[idx];
((List<ModCollection>)inheritor.DirectlyInheritsFrom).RemoveAt(idx);
((List<ModCollection>)parent.DirectParentOf).Remove(inheritor);
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
RecurseInheritanceChanges(inheritor);
Penumbra.Log.Debug($"Removed {parent.AnonymizedName} from {inheritor.AnonymizedName} inheritances.");
}
/// <summary> Order in the inheritance list is relevant. </summary>
public void MoveInheritance(ModCollection inheritor, int from, int to)
{
if (!((List<ModCollection>)inheritor.DirectlyInheritsFrom).Move(from, to))
return;
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
RecurseInheritanceChanges(inheritor);
Penumbra.Log.Debug($"Moved {inheritor.AnonymizedName}s inheritance {from} to {to}.");
}
/// <inheritdoc cref="AddInheritance(ModCollection, ModCollection)"/>
private bool AddInheritance(ModCollection inheritor, ModCollection parent, bool invokeEvent)
{
if (CheckValidInheritance(inheritor, parent) != ValidInheritance.Valid)
return false;
((List<ModCollection>)inheritor.DirectlyInheritsFrom).Add(parent);
((List<ModCollection>)parent.DirectParentOf).Add(inheritor);
if (invokeEvent)
{
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
RecurseInheritanceChanges(inheritor);
}
Penumbra.Log.Debug($"Added {parent.AnonymizedName} to {inheritor.AnonymizedName} inheritances.");
return true;
}
/// <summary>
/// Inheritances can not be setup before all collections are read,
/// so this happens after reading the collections in the constructor, consuming the stored strings.
/// </summary>
private void ApplyInheritances()
{
foreach (var (collection, inheritances, changes) in _storage.ConsumeInheritanceNames())
foreach (var (collection, directParents, changes) in _storage.ConsumeInheritanceNames())
{
var localChanges = changes;
foreach (var subCollection in inheritances)
foreach (var parent in directParents)
{
if (collection.AddInheritance(subCollection, false))
if (AddInheritance(collection, parent, false))
continue;
localChanges = true;
Penumbra.ChatService.NotificationMessage($"{collection.Name} can not inherit from {subCollection.Name}, removed.", "Warning",
Penumbra.ChatService.NotificationMessage($"{collection.Name} can not inherit from {parent.Name}, removed.", "Warning",
NotificationType.Warning);
}
@ -55,14 +143,22 @@ public class InheritanceManager : IDisposable
if (collectionType is not CollectionType.Inactive || old == null)
return;
foreach (var inheritance in old.Inheritance)
old.ClearSubscriptions(inheritance);
foreach (var c in _storage)
{
var inheritedIdx = c._inheritance.IndexOf(old);
var inheritedIdx = c.DirectlyInheritsFrom.IndexOf(old);
if (inheritedIdx >= 0)
c.RemoveInheritance(inheritedIdx);
RemoveInheritance(c, inheritedIdx);
((List<ModCollection>)c.DirectParentOf).Remove(old);
}
}
private void RecurseInheritanceChanges(ModCollection newInheritor)
{
foreach (var inheritor in newInheritor.DirectParentOf)
{
_communicator.CollectionInheritanceChanged.Invoke(inheritor, true);
RecurseInheritanceChanges(inheritor);
}
}
}

View file

@ -50,21 +50,12 @@ public class ModCollectionCache : IDisposable
public ModCollectionCache( ModCollection collection )
{
_collection = collection;
MetaManipulations = new MetaManager( _collection );
_collection.ModSettingChanged += OnModSettingChange;
_collection.InheritanceChanged += OnInheritanceChange;
if( !Penumbra.CharacterUtility.Ready )
{
Penumbra.CharacterUtility.LoadingFinished += IncrementCounter;
}
MetaManipulations = new MetaManager( _collection );
}
public void Dispose()
{
MetaManipulations.Dispose();
_collection.ModSettingChanged -= OnModSettingChange;
_collection.InheritanceChanged -= OnInheritanceChange;
Penumbra.CharacterUtility.LoadingFinished -= IncrementCounter;
}
// Resolve a given game path according to this collection.
@ -133,58 +124,6 @@ public class ModCollectionCache : IDisposable
return ret;
}
private void OnModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ )
{
switch( type )
{
case ModSettingChange.Inheritance:
ReloadMod( Penumbra.ModManager[ modIdx ], true );
break;
case ModSettingChange.EnableState:
if( oldValue == 0 )
{
AddMod( Penumbra.ModManager[ modIdx ], true );
}
else if( oldValue == 1 )
{
RemoveMod( Penumbra.ModManager[ modIdx ], true );
}
else if( _collection[ modIdx ].Settings?.Enabled == true )
{
ReloadMod( Penumbra.ModManager[ modIdx ], true );
}
else
{
RemoveMod( Penumbra.ModManager[ modIdx ], true );
}
break;
case ModSettingChange.Priority:
if( Conflicts( Penumbra.ModManager[ modIdx ] ).Count > 0 )
{
ReloadMod( Penumbra.ModManager[ modIdx ], true );
}
break;
case ModSettingChange.Setting:
if( _collection[ modIdx ].Settings?.Enabled == true )
{
ReloadMod( Penumbra.ModManager[ modIdx ], true );
}
break;
case ModSettingChange.MultiInheritance:
case ModSettingChange.MultiEnableState:
FullRecalculation(_collection == Penumbra.CollectionManager.Active.Default);
break;
}
}
// Inheritance changes are too big to check for relevance,
// just recompute everything.
private void OnInheritanceChange( bool _ )
=> FullRecalculation(_collection == Penumbra.CollectionManager.Active.Default);
public void FullRecalculation(bool isDefault)
{
ResolvedFiles.Clear();

View file

@ -1,138 +0,0 @@
using Penumbra.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using Penumbra.Api.Enums;
namespace Penumbra.Collections;
public partial class ModCollection
{
// If the change type is a bool, oldValue will be 1 for true and 0 for false.
// optionName will only be set for type == Setting.
public delegate void ModSettingChangeDelegate( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool inherited );
public event ModSettingChangeDelegate ModSettingChanged;
// Enable or disable the mod inheritance of mod idx.
public bool SetModInheritance( int idx, bool inherit )
{
if (!FixInheritance(idx, inherit))
return false;
ModSettingChanged.Invoke( ModSettingChange.Inheritance, idx, inherit ? 0 : 1, 0, false );
return true;
}
// Set the enabled state mod idx to newValue if it differs from the current enabled state.
// If mod idx is currently inherited, stop the inheritance.
public bool SetModState( int idx, bool newValue )
{
var oldValue = _settings[ idx ]?.Enabled ?? this[ idx ].Settings?.Enabled ?? false;
if (newValue == oldValue)
return false;
var inheritance = FixInheritance( idx, false );
_settings[ idx ]!.Enabled = newValue;
ModSettingChanged.Invoke( ModSettingChange.EnableState, idx, inheritance ? -1 : newValue ? 0 : 1, 0, false );
return true;
}
// Enable or disable the mod inheritance of every mod in mods.
public void SetMultipleModInheritances( IEnumerable< Mod > mods, bool inherit )
{
if( mods.Aggregate( false, ( current, mod ) => current | FixInheritance( mod.Index, inherit ) ) )
ModSettingChanged.Invoke( ModSettingChange.MultiInheritance, -1, -1, 0, false );
}
// Set the enabled state of every mod in mods to the new value.
// If the mod is currently inherited, stop the inheritance.
public void SetMultipleModStates( IEnumerable< Mod > mods, bool newValue )
{
var changes = false;
foreach( var mod in mods )
{
var oldValue = _settings[ mod.Index ]?.Enabled;
if (newValue == oldValue)
continue;
FixInheritance( mod.Index, false );
_settings[ mod.Index ]!.Enabled = newValue;
changes = true;
}
if( changes )
{
ModSettingChanged.Invoke( ModSettingChange.MultiEnableState, -1, -1, 0, false );
}
}
// Set the priority of mod idx to newValue if it differs from the current priority.
// If mod idx is currently inherited, stop the inheritance.
public bool SetModPriority( int idx, int newValue )
{
var oldValue = _settings[ idx ]?.Priority ?? this[ idx ].Settings?.Priority ?? 0;
if (newValue == oldValue)
return false;
var inheritance = FixInheritance( idx, false );
_settings[ idx ]!.Priority = newValue;
ModSettingChanged.Invoke( ModSettingChange.Priority, idx, inheritance ? -1 : oldValue, 0, false );
return true;
}
// Set a given setting group settingName of mod idx to newValue if it differs from the current value and fix it if necessary.
// If mod idx is currently inherited, stop the inheritance.
public bool SetModSetting( int idx, int groupIdx, uint newValue )
{
var settings = _settings[ idx ] != null ? _settings[ idx ]!.Settings : this[ idx ].Settings?.Settings;
var oldValue = settings?[ groupIdx ] ?? Penumbra.ModManager[idx].Groups[groupIdx].DefaultSettings;
if (oldValue == newValue)
return false;
var inheritance = FixInheritance( idx, false );
_settings[ idx ]!.SetValue( Penumbra.ModManager[ idx ], groupIdx, newValue );
ModSettingChanged.Invoke( ModSettingChange.Setting, idx, inheritance ? -1 : ( int )oldValue, groupIdx, false );
return true;
}
// Change one of the available mod settings for mod idx discerned by type.
// If type == Setting, settingName should be a valid setting for that mod, otherwise it will be ignored.
// The setting will also be automatically fixed if it is invalid for that setting group.
// For boolean parameters, newValue == 0 will be treated as false and != 0 as true.
public bool ChangeModSetting( ModSettingChange type, int idx, int newValue, int groupIdx )
{
return type switch
{
ModSettingChange.Inheritance => SetModInheritance( idx, newValue != 0 ),
ModSettingChange.EnableState => SetModState( idx, newValue != 0 ),
ModSettingChange.Priority => SetModPriority( idx, newValue ),
ModSettingChange.Setting => SetModSetting( idx, groupIdx, ( uint )newValue ),
_ => throw new ArgumentOutOfRangeException( nameof( type ), type, null ),
};
}
// Set inheritance of a mod without saving,
// to be used as an intermediary.
private bool FixInheritance( int idx, bool inherit )
{
var settings = _settings[ idx ];
if( inherit == ( settings == null ) )
return false;
_settings[ idx ] = inherit ? null : this[ idx ].Settings?.DeepCopy() ?? ModSettings.DefaultSettings( Penumbra.ModManager[ idx ] );
return true;
}
private void SaveOnChange( ModSettingChange _1, int _2, int _3, int _4, bool inherited )
=> SaveOnChange( inherited );
private void SaveOnChange( bool inherited )
{
if( !inherited )
Penumbra.SaveService.QueueSave(this);
}
}

View file

@ -32,7 +32,7 @@ public partial class ModCollection : ISavable
// Custom deserialization that is converted with the constructor.
var settings = obj[nameof(Settings)]?.ToObject<Dictionary<string, ModSettings.SavedSettings>>()
?? new Dictionary<string, ModSettings.SavedSettings>();
inheritance = obj[nameof(Inheritance)]?.ToObject<List<string>>() ?? (IReadOnlyList<string>)Array.Empty<string>();
inheritance = obj["Inheritance"]?.ToObject<List<string>>() ?? (IReadOnlyList<string>)Array.Empty<string>();
return new ModCollection(name, version, settings);
}
@ -86,8 +86,8 @@ public partial class ModCollection : ISavable
j.WriteEndObject();
// Inherit by collection name.
j.WritePropertyName(nameof(Inheritance));
x.Serialize(j, Inheritance.Select(c => c.Name));
j.WritePropertyName("Inheritance");
x.Serialize(j, DirectlyInheritsFrom.Select(c => c.Name));
j.WriteEndObject();
}
}

View file

@ -1,170 +0,0 @@
using OtterGui.Filesystem;
using Penumbra.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using Penumbra.Api.Enums;
namespace Penumbra.Collections;
// ModCollections can inherit from an arbitrary number of other collections.
// This is transitive, so a collection A inheriting from B also inherits from everything B inherits.
// Circular dependencies are resolved by distinctness.
public partial class ModCollection
{
// A change in inheritance usually requires complete recomputation.
// The bool signifies whether the change was in an already inherited collection.
public event Action< bool > InheritanceChanged;
internal readonly List< ModCollection > _inheritance = new();
public IReadOnlyList< ModCollection > Inheritance
=> _inheritance;
// Iterate over all collections inherited from in depth-first order.
// Skip already visited collections to avoid circular dependencies.
public IEnumerable< ModCollection > GetFlattenedInheritance()
=> InheritedCollections( this ).Distinct();
// All inherited collections in application order without filtering for duplicates.
private static IEnumerable< ModCollection > InheritedCollections( ModCollection collection )
=> collection.Inheritance.SelectMany( InheritedCollections ).Prepend( collection );
// Reasons why a collection can not be inherited from.
public enum ValidInheritance
{
Valid,
Self, // Can not inherit from self
Empty, // Can not inherit from the empty collection
Contained, // Already inherited from
Circle, // Inheritance would lead to a circle.
}
// Check whether a collection can be inherited from.
public ValidInheritance CheckValidInheritance( ModCollection? collection )
{
if( collection == null || ReferenceEquals( collection, Empty ) )
{
return ValidInheritance.Empty;
}
if( ReferenceEquals( collection, this ) )
{
return ValidInheritance.Self;
}
if( _inheritance.Contains( collection ) )
{
return ValidInheritance.Contained;
}
if( InheritedCollections( collection ).Any( c => c == this ) )
{
return ValidInheritance.Circle;
}
return ValidInheritance.Valid;
}
// Add a new collection to the inheritance list.
// We do not check if this collection would be visited before,
// only that it is unique in the list itself.
public bool AddInheritance( ModCollection collection, bool invokeEvent )
{
if( CheckValidInheritance( collection ) != ValidInheritance.Valid )
{
return false;
}
_inheritance.Add( collection );
// Changes in inherited collections may need to trigger further changes here.
collection.ModSettingChanged += OnInheritedModSettingChange;
collection.InheritanceChanged += OnInheritedInheritanceChange;
if( invokeEvent )
{
InheritanceChanged.Invoke( false );
}
Penumbra.Log.Debug( $"Added {collection.AnonymizedName} to {AnonymizedName} inheritances." );
return true;
}
public void RemoveInheritance( int idx )
{
var inheritance = _inheritance[ idx ];
ClearSubscriptions( inheritance );
_inheritance.RemoveAt( idx );
InheritanceChanged.Invoke( false );
Penumbra.Log.Debug( $"Removed {inheritance.AnonymizedName} from {AnonymizedName} inheritances." );
}
internal void ClearSubscriptions( ModCollection other )
{
other.ModSettingChanged -= OnInheritedModSettingChange;
other.InheritanceChanged -= OnInheritedInheritanceChange;
}
// Order in the inheritance list is relevant.
public void MoveInheritance( int from, int to )
{
if( _inheritance.Move( from, to ) )
{
InheritanceChanged.Invoke( false );
Penumbra.Log.Debug( $"Moved {AnonymizedName}s inheritance {from} to {to}." );
}
}
// Carry changes in collections inherited from forward if they are relevant for this collection.
private void OnInheritedModSettingChange( ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool _ )
{
switch( type )
{
case ModSettingChange.MultiInheritance:
case ModSettingChange.MultiEnableState:
ModSettingChanged.Invoke( type, modIdx, oldValue, groupIdx, true );
return;
default:
if( modIdx < 0 || modIdx >= _settings.Count )
{
Penumbra.Log.Warning(
$"Collection state broken, Mod {modIdx} in inheritance does not exist. ({_settings.Count} mods exist)." );
return;
}
if( _settings[ modIdx ] == null )
{
ModSettingChanged.Invoke( type, modIdx, oldValue, groupIdx, true );
}
return;
}
}
private void OnInheritedInheritanceChange( bool _ )
=> InheritanceChanged.Invoke( true );
// Obtain the actual settings for a given mod via index.
// Also returns the collection the settings are taken from.
// If no collection provides settings for this mod, this collection is returned together with null.
public (ModSettings? Settings, ModCollection Collection) this[ Index idx ]
{
get
{
if( Index <= 0 )
{
return ( ModSettings.Empty, this );
}
foreach( var collection in GetFlattenedInheritance() )
{
var settings = collection._settings[ idx ];
if( settings != null )
{
return ( settings, collection );
}
}
return ( null, this );
}
}
}

View file

@ -1,7 +1,6 @@
using Penumbra.Mods;
using System.Collections.Generic;
using System.Linq;
using Penumbra.Services;
using Penumbra.Util;
namespace Penumbra.Collections;

View file

@ -15,9 +15,9 @@ namespace Penumbra.Collections;
// - any change in settings or inheritance of the collection causes a Save.
public partial class ModCollection
{
public const int CurrentVersion = 1;
public const int CurrentVersion = 1;
public const string DefaultCollectionName = "Default";
public const string EmptyCollection = "None";
public const string EmptyCollection = "None";
public static readonly ModCollection Empty = CreateEmpty();
@ -51,18 +51,18 @@ public partial class ModCollection
=> Enumerable.Range(0, _settings.Count).Select(i => this[i].Settings);
// Settings for deleted mods will be kept via directory name.
private readonly Dictionary<string, ModSettings.SavedSettings> _unusedSettings;
internal readonly Dictionary<string, ModSettings.SavedSettings> _unusedSettings;
// Constructor for duplication.
private ModCollection(string name, ModCollection duplicate)
{
Name = name;
Version = duplicate.Version;
_settings = duplicate._settings.ConvertAll(s => s?.DeepCopy());
_unusedSettings = duplicate._unusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy());
_inheritance = duplicate._inheritance.ToList();
ModSettingChanged += SaveOnChange;
InheritanceChanged += SaveOnChange;
Name = name;
Version = duplicate.Version;
_settings = duplicate._settings.ConvertAll(s => s?.DeepCopy());
_unusedSettings = duplicate._unusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy());
DirectlyInheritsFrom = duplicate.DirectlyInheritsFrom.ToList();
foreach (var c in DirectlyInheritsFrom)
((List<ModCollection>)c.DirectParentOf).Add(this);
}
// Constructor for reading from files.
@ -76,8 +76,6 @@ public partial class ModCollection
ApplyModSettings();
Migration.Migrate(Penumbra.SaveService, this);
ModSettingChanged += SaveOnChange;
InheritanceChanged += SaveOnChange;
}
// Create a new, unique empty collection of a given name.
@ -87,11 +85,11 @@ public partial class ModCollection
// Create a new temporary collection that does not save and has a negative index.
public static ModCollection CreateNewTemporary(string name, int changeCounter)
{
var collection = new ModCollection(name, Empty);
collection.ModSettingChanged -= collection.SaveOnChange;
collection.InheritanceChanged -= collection.SaveOnChange;
collection.Index = ~Penumbra.TempCollections.Count;
collection.ChangeCounter = changeCounter;
var collection = new ModCollection(name, Empty)
{
Index = ~Penumbra.TempCollections.Count,
ChangeCounter = changeCounter,
};
collection.CreateCache(false);
return collection;
}
@ -162,55 +160,43 @@ public partial class ModCollection
Penumbra.SaveService.ImmediateSave(this);
}
public bool CopyModSettings(int modIdx, string modName, int targetIdx, string targetName)
{
if (targetName.Length == 0 && targetIdx < 0 || modName.Length == 0 && modIdx < 0)
return false;
// If the source mod exists, convert its settings to saved settings or null if its inheriting.
// If it does not exist, check unused settings.
// If it does not exist and has no unused settings, also use null.
ModSettings.SavedSettings? savedSettings = modIdx >= 0
? _settings[modIdx] != null
? new ModSettings.SavedSettings(_settings[modIdx]!, Penumbra.ModManager[modIdx])
: null
: _unusedSettings.TryGetValue(modName, out var s)
? s
: null;
if (targetIdx >= 0)
{
if (savedSettings != null)
{
// The target mod exists and the source settings are not inheriting, convert and fix the settings and copy them.
// This triggers multiple events.
savedSettings.Value.ToSettings(Penumbra.ModManager[targetIdx], out var settings);
SetModState(targetIdx, settings.Enabled);
SetModPriority(targetIdx, settings.Priority);
foreach (var (value, index) in settings.Settings.WithIndex())
SetModSetting(targetIdx, index, value);
}
else
{
// The target mod exists, but the source is inheriting, set the target to inheriting.
// This triggers events.
SetModInheritance(targetIdx, true);
}
}
else
{
// The target mod does not exist.
// Either copy the unused source settings directly if they are not inheriting,
// or remove any unused settings for the target if they are inheriting.
if (savedSettings != null)
_unusedSettings[targetName] = savedSettings.Value;
else
_unusedSettings.Remove(targetName);
}
return true;
}
public override string ToString()
=> Name;
/// <summary>
/// Obtain the actual settings for a given mod via index.
/// Also returns the collection the settings are taken from.
/// If no collection provides settings for this mod, this collection is returned together with null.
/// </summary>
public (ModSettings? Settings, ModCollection Collection) this[Index idx]
{
get
{
if (Index <= 0)
return (ModSettings.Empty, this);
foreach (var collection in GetFlattenedInheritance())
{
var settings = collection._settings[idx];
if (settings != null)
return (settings, collection);
}
return (null, this);
}
}
public readonly IReadOnlyList<ModCollection> DirectlyInheritsFrom = new List<ModCollection>();
public readonly IReadOnlyList<ModCollection> DirectParentOf = new List<ModCollection>();
/// <summary> All inherited collections in application order without filtering for duplicates. </summary>
public static IEnumerable<ModCollection> InheritedCollections(ModCollection collection)
=> collection.DirectlyInheritsFrom.SelectMany(InheritedCollections).Prepend(collection);
/// <summary>
/// Iterate over all collections inherited from in depth-first order.
/// Skip already visited collections to avoid circular dependencies.
/// </summary>
public IEnumerable<ModCollection> GetFlattenedInheritance()
=> InheritedCollections(this).Distinct();
}

View file

@ -8,7 +8,7 @@ using Dalamud.Game.Text.SeStringHandling;
using ImGuiNET;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors;
using Penumbra.Interop.Services;
using Penumbra.Mods;
@ -23,18 +23,20 @@ public class CommandHandler : IDisposable
{
private const string CommandName = "/penumbra";
private readonly CommandManager _commandManager;
private readonly RedrawService _redrawService;
private readonly ChatGui _chat;
private readonly Configuration _config;
private readonly ConfigWindow _configWindow;
private readonly ActorManager _actors;
private readonly ModManager _modManager;
private readonly CommandManager _commandManager;
private readonly RedrawService _redrawService;
private readonly ChatGui _chat;
private readonly Configuration _config;
private readonly ConfigWindow _configWindow;
private readonly ActorManager _actors;
private readonly ModManager _modManager;
private readonly CollectionManager _collectionManager;
private readonly Penumbra _penumbra;
private readonly Penumbra _penumbra;
private readonly CollectionEditor _collectionEditor;
public CommandHandler(Framework framework, CommandManager commandManager, ChatGui chat, RedrawService redrawService, Configuration config,
ConfigWindow configWindow, ModManager modManager, CollectionManager collectionManager, ActorService actors, Penumbra penumbra)
ConfigWindow configWindow, ModManager modManager, CollectionManager collectionManager, ActorService actors, Penumbra penumbra,
CollectionEditor collectionEditor)
{
_commandManager = commandManager;
_redrawService = redrawService;
@ -45,6 +47,7 @@ public class CommandHandler : IDisposable
_actors = actors.AwaitedService;
_chat = chat;
_penumbra = penumbra;
_collectionEditor = collectionEditor;
framework.RunOnFrameworkThread(() =>
{
_commandManager.AddHandler(CommandName, new CommandInfo(OnCommand)
@ -455,14 +458,15 @@ public class CommandHandler : IDisposable
collection = string.Equals(lowerName, ModCollection.Empty.Name, StringComparison.OrdinalIgnoreCase)
? ModCollection.Empty
: _collectionManager.Storage.ByName(lowerName, out var c) ? c : null;
: _collectionManager.Storage.ByName(lowerName, out var c)
? c
: null;
if (collection != null)
return true;
_chat.Print(new SeStringBuilder().AddText("The collection ").AddRed(collectionName, true).AddText(" does not exist.")
.BuiltString);
return false;
}
private static bool? ParseTrueFalseToggle(string value)
@ -498,51 +502,47 @@ public class CommandHandler : IDisposable
private bool HandleModState(int settingState, ModCollection collection, Mod mod)
{
var settings = collection!.Settings[mod.Index];
var settings = collection.Settings[mod.Index];
switch (settingState)
{
case 0:
if (collection.SetModState(mod.Index, true))
{
Print(() => new SeStringBuilder().AddText("Enabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddText(".").BuiltString);
return true;
}
if (!_collectionEditor.SetModState(collection, mod, true))
return false;
Print(() => new SeStringBuilder().AddText("Enabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddText(".").BuiltString);
return true;
return false;
case 1:
if (collection.SetModState(mod.Index, false))
{
Print(() => new SeStringBuilder().AddText("Disabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddText(".").BuiltString);
return true;
}
if (!_collectionEditor.SetModState(collection, mod, false))
return false;
Print(() => new SeStringBuilder().AddText("Disabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddText(".").BuiltString);
return true;
return false;
case 2:
var setting = !(settings?.Enabled ?? false);
if (collection.SetModState(mod.Index, setting))
{
Print(() => new SeStringBuilder().AddText(setting ? "Enabled mod " : "Disabled mod ").AddPurple(mod.Name, true)
.AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddText(".").BuiltString);
return true;
}
if (!_collectionEditor.SetModState(collection, mod, setting))
return false;
Print(() => new SeStringBuilder().AddText(setting ? "Enabled mod " : "Disabled mod ").AddPurple(mod.Name, true)
.AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddText(".").BuiltString);
return true;
return false;
case 3:
if (collection.SetModInheritance(mod.Index, true))
{
Print(() => new SeStringBuilder().AddText("Set mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddText(" to inherit.").BuiltString);
return true;
}
if (!_collectionEditor.SetModInheritance(collection, mod, true))
return false;
Print(() => new SeStringBuilder().AddText("Set mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddText(" to inherit.").BuiltString);
return true;
return false;
}
return false;

View file

@ -253,7 +253,7 @@ public class Penumbra : IDalamudPlugin
void PrintCollection(ModCollection c)
=> sb.Append($"**Collection {c.AnonymizedName}**\n"
+ $"> **`Inheritances: `** {c.Inheritance.Count}\n"
+ $"> **`Inheritances: `** {c.DirectlyInheritsFrom.Count}\n"
+ $"> **`Enabled Mods: `** {c.ActualSettings.Count(s => s is { Enabled: true })}\n"
+ $"> **`Conflicts (Solved/Total): `** {c.AllConflicts.SelectMany(x => x).Sum(x => x.HasPriority && x.Solved ? x.Conflicts.Count : 0)}/{c.AllConflicts.SelectMany(x => x).Sum(x => x.HasPriority ? x.Conflicts.Count : 0)}\n");

View file

@ -91,6 +91,7 @@ public class PenumbraNew
.AddSingleton<InheritanceManager>()
.AddSingleton<CollectionCacheManager>()
.AddSingleton<TempCollectionManager>()
.AddSingleton<CollectionEditor>()
.AddSingleton<CollectionManager>();
// Add Mod Services

View file

@ -1,5 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Mods;
@ -171,6 +173,44 @@ public sealed class ModPathChanged : EventWrapper<Action<ModPathChangeType, Mod,
=> Invoke(this, changeType, mod, oldModDirectory, newModDirectory);
}
/// <summary>
/// Triggered whenever a mod setting is changed.
/// <list type="number">
/// <item>Parameter is the collection in which the setting was changed. </item>
/// <item>Parameter is the type of change. </item>
/// <item>Parameter is the mod the setting was changed for, unless it was a multi-change. </item>
/// <item>Parameter is the old value of the setting before the change as int. </item>
/// <item>Parameter is the index of the changed group if the change type is Setting. </item>
/// <item>Parameter is whether the change was inherited from another collection. </item>
/// </list>
/// </summary>
public sealed class ModSettingChanged : EventWrapper<Action<ModCollection, ModSettingChange, Mod?, int, int, bool>>
{
public ModSettingChanged()
: base(nameof(ModSettingChanged))
{ }
public void Invoke(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool inherited)
=> Invoke(this, collection, type, mod, oldValue, groupIdx, inherited);
}
/// <summary>
/// Triggered whenever a collections inheritances change.
/// <list type="number">
/// <item>Parameter is the collection whose ancestors were changed. </item>
/// <item>Parameter is whether the change was itself inherited, i.e. if it happened in a direct parent (false) or a more removed ancestor (true). </item>
/// </list>
/// </summary>
public sealed class CollectionInheritanceChanged : EventWrapper<Action<ModCollection, bool>>
{
public CollectionInheritanceChanged()
: base(nameof(CollectionInheritanceChanged))
{ }
public void Invoke(ModCollection collection, bool inherited)
=> Invoke(this, collection, inherited);
}
public class CommunicatorService : IDisposable
{
/// <inheritdoc cref="Services.CollectionChange"/>
@ -179,30 +219,36 @@ public class CommunicatorService : IDisposable
/// <inheritdoc cref="Services.TemporaryGlobalModChange"/>
public readonly TemporaryGlobalModChange TemporaryGlobalModChange = new();
/// <inheritdoc cref="Services.CreatingCharacterBase "/>
/// <inheritdoc cref="Services.CreatingCharacterBase"/>
public readonly CreatingCharacterBase CreatingCharacterBase = new();
/// <inheritdoc cref="Services.CreatedCharacterBase "/>
/// <inheritdoc cref="Services.CreatedCharacterBase"/>
public readonly CreatedCharacterBase CreatedCharacterBase = new();
/// <inheritdoc cref="Services.ModDataChanged "/>
/// <inheritdoc cref="Services.ModDataChanged"/>
public readonly ModDataChanged ModDataChanged = new();
/// <inheritdoc cref="Services.ModOptionChanged "/>
/// <inheritdoc cref="Services.ModOptionChanged"/>
public readonly ModOptionChanged ModOptionChanged = new();
/// <inheritdoc cref="Services.ModDiscoveryStarted "/>
/// <inheritdoc cref="Services.ModDiscoveryStarted"/>
public readonly ModDiscoveryStarted ModDiscoveryStarted = new();
/// <inheritdoc cref="Services.ModDiscoveryFinished "/>
/// <inheritdoc cref="Services.ModDiscoveryFinished"/>
public readonly ModDiscoveryFinished ModDiscoveryFinished = new();
/// <inheritdoc cref="Services.ModDirectoryChanged "/>
/// <inheritdoc cref="Services.ModDirectoryChanged"/>
public readonly ModDirectoryChanged ModDirectoryChanged = new();
/// <inheritdoc cref="Services.ModPathChanged "/>
/// <inheritdoc cref="Services.ModPathChanged"/>
public readonly ModPathChanged ModPathChanged = new();
/// <inheritdoc cref="Services.ModSettingChanged"/>
public readonly ModSettingChanged ModSettingChanged = new();
/// <inheritdoc cref="Services.CollectionInheritanceChanged"/>
public readonly CollectionInheritanceChanged CollectionInheritanceChanged = new();
public void Dispose()
{
CollectionChange.Dispose();
@ -215,5 +261,7 @@ public class CommunicatorService : IDisposable
ModDiscoveryFinished.Dispose();
ModDirectoryChanged.Dispose();
ModPathChanged.Dispose();
ModSettingChanged.Dispose();
CollectionInheritanceChanged.Dispose();
}
}

View file

@ -200,7 +200,7 @@ public class ConfigMigrationService
if (jObject[nameof(ModCollection.Name)]?.ToObject<string>() == ForcedCollection)
continue;
jObject[nameof(ModCollection.Inheritance)] = JToken.FromObject(new List<string> { ForcedCollection });
jObject[nameof(ModCollection.DirectlyInheritsFrom)] = JToken.FromObject(new List<string> { ForcedCollection });
File.WriteAllText(collection.FullName, jObject.ToString());
}
catch (Exception e)

View file

@ -57,7 +57,8 @@ public class ItemSwapTab : IDisposable, ITab
};
_communicator.CollectionChange.Subscribe(OnCollectionChange);
_collectionManager.Active.Current.ModSettingChanged += OnSettingChange;
_communicator.ModSettingChanged.Subscribe(OnSettingChange);
_communicator.CollectionInheritanceChanged.Subscribe(OnInheritanceChange);
_communicator.ModOptionChanged.Subscribe(OnModOptionChange);
}
@ -102,7 +103,8 @@ public class ItemSwapTab : IDisposable, ITab
public void Dispose()
{
_communicator.CollectionChange.Unsubscribe(OnCollectionChange);
_collectionManager.Active.Current.ModSettingChanged -= OnSettingChange;
_communicator.ModSettingChanged.Unsubscribe(OnSettingChange);
_communicator.CollectionInheritanceChanged.Unsubscribe(OnInheritanceChange);
_communicator.ModOptionChanged.Unsubscribe(OnModOptionChange);
}
@ -744,21 +746,29 @@ public class ItemSwapTab : IDisposable, ITab
if (collectionType != CollectionType.Current || _mod == null || newCollection == null)
return;
UpdateMod(_mod, _mod.Index < newCollection.Settings.Count ? newCollection.Settings[_mod.Index] : null);
newCollection.ModSettingChanged += OnSettingChange;
if (oldCollection != null)
oldCollection.ModSettingChanged -= OnSettingChange;
UpdateMod(_mod, _mod.Index < newCollection.Settings.Count ? newCollection[_mod.Index].Settings : null);
}
private void OnSettingChange(ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool inherited)
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool inherited)
{
if (modIdx != _mod?.Index)
if (collection != _collectionManager.Active.Current || mod != _mod)
return;
_swapData.LoadMod(_mod, _modSettings);
_dirty = true;
}
private void OnInheritanceChange(ModCollection collection, bool _)
{
if (collection != _collectionManager.Active.Current || _mod == null)
return;
UpdateMod(_mod, collection[_mod.Index].Settings);
_swapData.LoadMod(_mod, _modSettings);
_dirty = true;
}
private void OnModOptionChange(ModOptionChangeType type, Mod mod, int a, int b, int c)
{
if (type is ModOptionChangeType.PrepareChange || mod != _mod)

View file

@ -17,7 +17,7 @@ public class InheritanceUi
private const int InheritedCollectionHeight = 9;
private const string InheritanceDragDropLabel = "##InheritanceMove";
private readonly CollectionManager _collectionManager;
private readonly CollectionManager _collectionManager;
public InheritanceUi(CollectionManager collectionManager)
=> _collectionManager = collectionManager;
@ -118,7 +118,7 @@ public class InheritanceUi
_seenInheritedCollections.Clear();
_seenInheritedCollections.Add(_collectionManager.Active.Current);
foreach (var collection in _collectionManager.Active.Current.Inheritance.ToList())
foreach (var collection in _collectionManager.Active.Current.DirectlyInheritsFrom.ToList())
DrawInheritance(collection);
}
@ -136,7 +136,7 @@ public class InheritanceUi
using var target = ImRaii.DragDropTarget();
if (target.Success && ImGuiUtil.IsDropping(InheritanceDragDropLabel))
_inheritanceAction = (_collectionManager.Active.Current.Inheritance.IndexOf(_movedInheritance!), -1);
_inheritanceAction = (_collectionManager.Active.Current.DirectlyInheritsFrom.IndexOf(_movedInheritance!), -1);
}
/// <summary>
@ -157,9 +157,9 @@ public class InheritanceUi
if (_inheritanceAction.Value.Item1 >= 0)
{
if (_inheritanceAction.Value.Item2 == -1)
_collectionManager.Active.Current.RemoveInheritance(_inheritanceAction.Value.Item1);
_collectionManager.Inheritances.RemoveInheritance(_collectionManager.Active.Current, _inheritanceAction.Value.Item1);
else
_collectionManager.Active.Current.MoveInheritance(_inheritanceAction.Value.Item1, _inheritanceAction.Value.Item2);
_collectionManager.Inheritances.MoveInheritance(_collectionManager.Active.Current, _inheritanceAction.Value.Item1, _inheritanceAction.Value.Item2);
}
_inheritanceAction = null;
@ -173,22 +173,22 @@ public class InheritanceUi
{
DrawNewInheritanceCombo();
ImGui.SameLine();
var inheritance = _collectionManager.Active.Current.CheckValidInheritance(_newInheritance);
var inheritance = InheritanceManager.CheckValidInheritance(_collectionManager.Active.Current, _newInheritance);
var tt = inheritance switch
{
ModCollection.ValidInheritance.Empty => "No valid collection to inherit from selected.",
ModCollection.ValidInheritance.Valid => $"Let the {TutorialService.SelectedCollection} inherit from this collection.",
ModCollection.ValidInheritance.Self => "The collection can not inherit from itself.",
ModCollection.ValidInheritance.Contained => "Already inheriting from this collection.",
ModCollection.ValidInheritance.Circle => "Inheriting from this collection would lead to cyclic inheritance.",
InheritanceManager.ValidInheritance.Empty => "No valid collection to inherit from selected.",
InheritanceManager.ValidInheritance.Valid => $"Let the {TutorialService.SelectedCollection} inherit from this collection.",
InheritanceManager.ValidInheritance.Self => "The collection can not inherit from itself.",
InheritanceManager.ValidInheritance.Contained => "Already inheriting from this collection.",
InheritanceManager.ValidInheritance.Circle => "Inheriting from this collection would lead to cyclic inheritance.",
_ => string.Empty,
};
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), UiHelpers.IconButtonSize, tt,
inheritance != ModCollection.ValidInheritance.Valid, true)
&& _collectionManager.Active.Current.AddInheritance(_newInheritance!, true))
inheritance != InheritanceManager.ValidInheritance.Valid, true)
&& _collectionManager.Inheritances.AddInheritance(_collectionManager.Active.Current, _newInheritance!))
_newInheritance = null;
if (inheritance != ModCollection.ValidInheritance.Valid)
if (inheritance != InheritanceManager.ValidInheritance.Valid)
_newInheritance = null;
ImGui.SameLine();
@ -234,14 +234,14 @@ public class InheritanceUi
{
ImGui.SetNextItemWidth(UiHelpers.InputTextMinusButton);
_newInheritance ??= _collectionManager.Storage.FirstOrDefault(c
=> c != _collectionManager.Active.Current && !_collectionManager.Active.Current.Inheritance.Contains(c))
=> c != _collectionManager.Active.Current && !_collectionManager.Active.Current.DirectlyInheritsFrom.Contains(c))
?? ModCollection.Empty;
using var combo = ImRaii.Combo("##newInheritance", _newInheritance.Name);
if (!combo)
return;
foreach (var collection in _collectionManager.Storage
.Where(c => _collectionManager.Active.Current.CheckValidInheritance(c) == ModCollection.ValidInheritance.Valid)
.Where(c => InheritanceManager.CheckValidInheritance(_collectionManager.Active.Current, c) == InheritanceManager.ValidInheritance.Valid)
.OrderBy(c => c.Name))
{
if (ImGui.Selectable(collection.Name, _newInheritance == collection))
@ -261,8 +261,8 @@ public class InheritanceUi
if (_movedInheritance != null)
{
var idx1 = _collectionManager.Active.Current.Inheritance.IndexOf(_movedInheritance);
var idx2 = _collectionManager.Active.Current.Inheritance.IndexOf(collection);
var idx1 = _collectionManager.Active.Current.DirectlyInheritsFrom.IndexOf(_movedInheritance);
var idx2 = _collectionManager.Active.Current.DirectlyInheritsFrom.IndexOf(collection);
if (idx1 >= 0 && idx2 >= 0)
_inheritanceAction = (idx1, idx2);
}
@ -292,7 +292,7 @@ public class InheritanceUi
if (ImGui.GetIO().KeyCtrl && ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
if (withDelete && ImGui.GetIO().KeyShift)
_inheritanceAction = (_collectionManager.Active.Current.Inheritance.IndexOf(collection), -1);
_inheritanceAction = (_collectionManager.Active.Current.DirectlyInheritsFrom.IndexOf(collection), -1);
else
_newCurrentCollection = collection;
}

View file

@ -73,9 +73,9 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
SetFilterTooltip();
SelectionChanged += OnSelectionChange;
_communicator.CollectionChange.Subscribe(OnCollectionChange);
_collectionManager.Active.Current.ModSettingChanged += OnSettingChange;
_collectionManager.Active.Current.InheritanceChanged += OnInheritanceChange;
_communicator.CollectionChange.Subscribe(OnCollectionChange);
_communicator.ModSettingChanged.Subscribe(OnSettingChange);
_communicator.CollectionInheritanceChanged.Subscribe(OnInheritanceChange);
_communicator.ModDataChanged.Subscribe(OnModDataChange);
_communicator.ModDiscoveryStarted.Subscribe(StoreCurrentSelection);
_communicator.ModDiscoveryFinished.Subscribe(RestoreLastSelection);
@ -88,8 +88,8 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
_communicator.ModDiscoveryStarted.Unsubscribe(StoreCurrentSelection);
_communicator.ModDiscoveryFinished.Unsubscribe(RestoreLastSelection);
_communicator.ModDataChanged.Unsubscribe(OnModDataChange);
_collectionManager.Active.Current.ModSettingChanged -= OnSettingChange;
_collectionManager.Active.Current.InheritanceChanged -= OnInheritanceChange;
_communicator.ModSettingChanged.Unsubscribe(OnSettingChange);
_communicator.CollectionInheritanceChanged.Unsubscribe(OnInheritanceChange);
_communicator.CollectionChange.Unsubscribe(OnCollectionChange);
}
@ -267,9 +267,9 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
});
if (inherit)
_collectionManager.Active.Current.SetMultipleModInheritances(mods, enabled);
_collectionManager.Editor.SetMultipleModInheritances(_collectionManager.Active.Current, mods, enabled);
else
_collectionManager.Active.Current.SetMultipleModStates(mods, enabled);
_collectionManager.Editor.SetMultipleModStates(_collectionManager.Active.Current, mods, enabled);
}
/// <summary>
@ -360,11 +360,13 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
#region Automatic cache update functions.
private void OnSettingChange(ModSettingChange type, int modIdx, int oldValue, int groupIdx, bool inherited)
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool inherited)
{
// TODO: maybe make more efficient
if (collection != _collectionManager.Active.Current)
return;
SetFilterDirty();
if (modIdx == Selected?.Index)
if (mod == Selected)
OnSelectionChange(Selected, Selected, default);
}
@ -382,8 +384,11 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
}
}
private void OnInheritanceChange(bool _)
private void OnInheritanceChange(ModCollection collection, bool _)
{
if (collection != _collectionManager.Active.Current)
return;
SetFilterDirty();
OnSelectionChange(Selected, Selected, default);
}
@ -393,18 +398,6 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
if (collectionType != CollectionType.Current || oldCollection == newCollection)
return;
if (oldCollection != null)
{
oldCollection.ModSettingChanged -= OnSettingChange;
oldCollection.InheritanceChanged -= OnInheritanceChange;
}
if (newCollection != null)
{
newCollection.ModSettingChanged += OnSettingChange;
newCollection.InheritanceChanged += OnInheritanceChange;
}
SetFilterDirty();
OnSelectionChange(Selected, Selected, default);
}

View file

@ -46,10 +46,10 @@ public class ModPanelHeader : IDisposable
}
// Author
var author = mod.Author.IsEmpty ? string.Empty : $"by {mod.Author}";
if (author != _modAuthor)
if (mod.Author != _modAuthor)
{
_modAuthor = author;
var author = mod.Author.IsEmpty ? string.Empty : $"by {mod.Author}";
_modAuthor = mod.Author.Text;
_modAuthorWidth = ImGui.CalcTextSize(author).X;
_secondRowWidth = _modAuthorWidth + _modWebsiteButtonWidth + ImGui.GetStyle().ItemSpacing.X;
}

View file

@ -20,11 +20,11 @@ namespace Penumbra.UI.ModsTab;
public class ModPanelSettingsTab : ITab
{
private readonly Configuration _config;
private readonly CollectionManager _collectionManager;
private readonly CollectionManager _collectionManager;
private readonly ModFileSystemSelector _selector;
private readonly TutorialService _tutorial;
private readonly PenumbraApi _api;
private readonly ModManager _modManager;
private readonly ModManager _modManager;
private bool _inherited;
private ModSettings _settings = null!;
@ -114,7 +114,7 @@ public class ModPanelSettingsTab : ITab
using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.PressEnterWarningBg);
var width = new Vector2(ImGui.GetContentRegionAvail().X, 0);
if (ImGui.Button($"These settings are inherited from {_collection.Name}.", width))
_collectionManager.Active.Current.SetModInheritance(_selector.Selected!.Index, false);
_collectionManager.Editor.SetModInheritance(_collectionManager.Active.Current, _selector.Selected!, false);
ImGuiUtil.HoverTooltip("You can click this button to copy the current settings to the current selection.\n"
+ "You can also just change any setting, which will copy the settings with the single setting changed to the current selection.");
@ -128,7 +128,7 @@ public class ModPanelSettingsTab : ITab
return;
_modManager.SetKnown(_selector.Selected!);
_collectionManager.Active.Current.SetModState(_selector.Selected!.Index, enabled);
_collectionManager.Editor.SetModState(_collectionManager.Active.Current, _selector.Selected!, enabled);
}
/// <summary>
@ -146,7 +146,7 @@ public class ModPanelSettingsTab : ITab
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
{
if (_currentPriority != _settings.Priority)
_collectionManager.Active.Current.SetModPriority(_selector.Selected!.Index, _currentPriority.Value);
_collectionManager.Editor.SetModPriority(_collectionManager.Active.Current, _selector.Selected!, _currentPriority.Value);
_currentPriority = null;
}
@ -168,7 +168,7 @@ public class ModPanelSettingsTab : ITab
var scroll = ImGui.GetScrollMaxY() > 0 ? ImGui.GetStyle().ScrollbarSize : 0;
ImGui.SameLine(ImGui.GetWindowWidth() - ImGui.CalcTextSize(text).X - ImGui.GetStyle().FramePadding.X * 2 - scroll);
if (ImGui.Button(text))
_collectionManager.Active.Current.SetModInheritance(_selector.Selected!.Index, true);
_collectionManager.Editor.SetModInheritance(_collectionManager.Active.Current, _selector.Selected!, true);
ImGuiUtil.HoverTooltip("Remove current settings from this collection so that it can inherit them.\n"
+ "If no inherited collection has settings for this mod, it will be disabled.");
@ -191,7 +191,7 @@ public class ModPanelSettingsTab : ITab
id.Push(idx2);
var option = group[idx2];
if (ImGui.Selectable(option.Name, idx2 == selectedOption))
_collectionManager.Active.Current.SetModSetting(_selector.Selected!.Index, groupIdx, (uint)idx2);
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, (uint)idx2);
if (option.Description.Length > 0)
{
@ -227,7 +227,7 @@ public class ModPanelSettingsTab : ITab
{
using var id = ImRaii.PushId(groupIdx);
var selectedOption = _empty ? (int)group.DefaultSettings : (int)_settings.Settings[groupIdx];
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
void DrawOptions()
{
@ -236,7 +236,7 @@ public class ModPanelSettingsTab : ITab
using var i = ImRaii.PushId(idx);
var option = group[idx];
if (ImGui.RadioButton(option.Name, selectedOption == idx))
_collectionManager.Active.Current.SetModSetting(_selector.Selected!.Index, groupIdx, (uint)idx);
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, (uint)idx);
if (option.Description.Length <= 0)
continue;
@ -264,7 +264,7 @@ public class ModPanelSettingsTab : ITab
var shown = ImGui.GetStateStorage().GetBool(collapseId, true);
var buttonTextShow = $"Show {group.Count} Options";
var buttonTextHide = $"Hide {group.Count} Options";
var buttonWidth = Math.Max(ImGui.CalcTextSize(buttonTextShow).X, ImGui.CalcTextSize(buttonTextHide).X)
var buttonWidth = Math.Max(ImGui.CalcTextSize(buttonTextShow).X, ImGui.CalcTextSize(buttonTextHide).X)
+ 2 * ImGui.GetStyle().FramePadding.X;
minWidth = Math.Max(buttonWidth, minWidth);
if (shown)
@ -276,10 +276,9 @@ public class ModPanelSettingsTab : ITab
draw();
}
var width = Math.Max(ImGui.GetItemRectSize().X, minWidth);
var endPos = ImGui.GetCursorPos();
var width = Math.Max(ImGui.GetItemRectSize().X, minWidth);
var endPos = ImGui.GetCursorPos();
ImGui.SetCursorPos(pos);
if (ImGui.Button(buttonTextHide, new Vector2(width, 0)))
ImGui.GetStateStorage().SetBool(collapseId, !shown);
@ -292,7 +291,7 @@ public class ModPanelSettingsTab : ITab
+ ImGui.GetStyle().ItemInnerSpacing.X
+ ImGui.GetFrameHeight()
+ ImGui.GetStyle().FramePadding.X;
var width = Math.Max(optionWidth, minWidth);
var width = Math.Max(optionWidth, minWidth);
if (ImGui.Button(buttonTextShow, new Vector2(width, 0)))
ImGui.GetStateStorage().SetBool(collapseId, !shown);
}
@ -305,9 +304,9 @@ public class ModPanelSettingsTab : ITab
/// </summary>
private void DrawMultiGroup(IModGroup group, int groupIdx)
{
using var id = ImRaii.PushId(groupIdx);
var flags = _empty ? group.DefaultSettings : _settings.Settings[groupIdx];
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
using var id = ImRaii.PushId(groupIdx);
var flags = _empty ? group.DefaultSettings : _settings.Settings[groupIdx];
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
void DrawOptions()
{
@ -321,7 +320,7 @@ public class ModPanelSettingsTab : ITab
if (ImGui.Checkbox(option.Name, ref setting))
{
flags = setting ? flags | flag : flags & ~flag;
_collectionManager.Active.Current.SetModSetting(_selector.Selected!.Index, groupIdx, flags);
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, flags);
}
if (option.Description.Length > 0)
@ -349,10 +348,10 @@ public class ModPanelSettingsTab : ITab
if (ImGui.Selectable("Enable All"))
{
flags = group.Count == 32 ? uint.MaxValue : (1u << group.Count) - 1u;
_collectionManager.Active.Current.SetModSetting(_selector.Selected!.Index, groupIdx, flags);
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, flags);
}
if (ImGui.Selectable("Disable All"))
_collectionManager.Active.Current.SetModSetting(_selector.Selected!.Index, groupIdx, 0);
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, 0);
}
}

View file

@ -91,7 +91,7 @@ public class ModsTab : ITab
+ $"{_selector.SortMode.Name} Sort Mode\n"
+ $"{_selector.SelectedLeaf?.Name ?? "NULL"} Selected Leaf\n"
+ $"{_selector.Selected?.Name ?? "NULL"} Selected Mod\n"
+ $"{string.Join(", ", _collectionManager.Active.Current.Inheritance.Select(c => c.AnonymizedName))} Inheritances\n"
+ $"{string.Join(", ", _collectionManager.Active.Current.DirectlyInheritsFrom.Select(c => c.AnonymizedName))} Inheritances\n"
+ $"{_selector.SelectedSettingCollection.AnonymizedName} Collection\n");
}
}