mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Turn Settings and Priority into their own types.
This commit is contained in:
parent
77bf441e62
commit
b1ca073276
29 changed files with 422 additions and 298 deletions
|
|
@ -27,6 +27,7 @@ using Penumbra.UI;
|
|||
using TextureType = Penumbra.Api.Enums.TextureType;
|
||||
using Penumbra.Interop.ResourceTree;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
|
||||
namespace Penumbra.Api;
|
||||
|
||||
|
|
@ -39,13 +40,13 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
{
|
||||
add => _communicator.PreSettingsTabBarDraw.Subscribe(value!, Communication.PreSettingsTabBarDraw.Priority.Default);
|
||||
remove => _communicator.PreSettingsTabBarDraw.Unsubscribe(value!);
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<string>? PreSettingsPanelDraw
|
||||
{
|
||||
add => _communicator.PreSettingsPanelDraw.Subscribe(value!, Communication.PreSettingsPanelDraw.Priority.Default);
|
||||
remove => _communicator.PreSettingsPanelDraw.Unsubscribe(value!);
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<string>? PostEnabledDraw
|
||||
{
|
||||
|
|
@ -649,7 +650,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
|
||||
var shareSettings = settings.ConvertToShareable(mod);
|
||||
return (PenumbraApiEc.Success,
|
||||
(shareSettings.Enabled, shareSettings.Priority, shareSettings.Settings, collection.Settings[mod.Index] != null));
|
||||
(shareSettings.Enabled, shareSettings.Priority.Value, shareSettings.Settings, collection.Settings[mod.Index] != null));
|
||||
}
|
||||
|
||||
public PenumbraApiEc ReloadMod(string modDirectory, string modName)
|
||||
|
|
@ -791,7 +792,9 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
if (!_modManager.TryGetMod(modDirectory, modName, out var mod))
|
||||
return PenumbraApiEc.ModMissing;
|
||||
|
||||
return _collectionEditor.SetModPriority(collection, mod, priority) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
|
||||
return _collectionEditor.SetModPriority(collection, mod, new ModPriority(priority))
|
||||
? PenumbraApiEc.Success
|
||||
: PenumbraApiEc.NothingChanged;
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetModSetting(string collectionName, string modDirectory, string modName, string optionGroupName,
|
||||
|
|
@ -820,7 +823,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
Args("CollectionName", collectionName, "ModDirectory", modDirectory, "ModName", modName, "OptionGroupName", optionGroupName,
|
||||
"OptionName", optionName));
|
||||
|
||||
var setting = mod.Groups[groupIdx].Type == GroupType.Multi ? 1u << optionIdx : (uint)optionIdx;
|
||||
var setting = mod.Groups[groupIdx].Type switch
|
||||
{
|
||||
GroupType.Multi => Setting.Multi(optionIdx),
|
||||
GroupType.Single => Setting.Single(optionIdx),
|
||||
_ => Setting.Zero,
|
||||
};
|
||||
|
||||
return Return(
|
||||
_collectionEditor.SetModSetting(collection, mod, groupIdx, setting) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged,
|
||||
|
|
@ -850,7 +858,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
|
||||
var group = mod.Groups[groupIdx];
|
||||
|
||||
uint setting = 0;
|
||||
var setting = Setting.Zero;
|
||||
if (group.Type == GroupType.Single)
|
||||
{
|
||||
var optionIdx = optionNames.Count == 0 ? -1 : group.IndexOf(o => o.Name == optionNames[^1]);
|
||||
|
|
@ -859,7 +867,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
Args("CollectionName", collectionName, "ModDirectory", modDirectory, "ModName", modName, "OptionGroupName", optionGroupName,
|
||||
"#optionNames", optionNames.Count.ToString()));
|
||||
|
||||
setting = (uint)optionIdx;
|
||||
setting = Setting.Single(optionIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -871,7 +879,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
Args("CollectionName", collectionName, "ModDirectory", modDirectory, "ModName", modName, "OptionGroupName",
|
||||
optionGroupName, "#optionNames", optionNames.Count.ToString()));
|
||||
|
||||
setting |= 1u << optionIdx;
|
||||
setting |= Setting.Multi(optionIdx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -993,7 +1001,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
if (!ConvertManips(manipString, out var m))
|
||||
return PenumbraApiEc.InvalidManipulation;
|
||||
|
||||
return _tempMods.Register(tag, null, p, m, priority) switch
|
||||
return _tempMods.Register(tag, null, p, m, new ModPriority(priority)) switch
|
||||
{
|
||||
RedirectResult.Success => PenumbraApiEc.Success,
|
||||
_ => PenumbraApiEc.UnknownError,
|
||||
|
|
@ -1014,7 +1022,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
if (!ConvertManips(manipString, out var m))
|
||||
return PenumbraApiEc.InvalidManipulation;
|
||||
|
||||
return _tempMods.Register(tag, collection, p, m, priority) switch
|
||||
return _tempMods.Register(tag, collection, p, m, new ModPriority(priority)) switch
|
||||
{
|
||||
RedirectResult.Success => PenumbraApiEc.Success,
|
||||
_ => PenumbraApiEc.UnknownError,
|
||||
|
|
@ -1024,7 +1032,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
public PenumbraApiEc RemoveTemporaryModAll(string tag, int priority)
|
||||
{
|
||||
CheckInitialized();
|
||||
return _tempMods.Unregister(tag, null, priority) switch
|
||||
return _tempMods.Unregister(tag, null, new ModPriority(priority)) switch
|
||||
{
|
||||
RedirectResult.Success => PenumbraApiEc.Success,
|
||||
RedirectResult.NotRegistered => PenumbraApiEc.NothingChanged,
|
||||
|
|
@ -1039,7 +1047,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
&& !_collectionManager.Storage.ByName(collectionName, out collection))
|
||||
return PenumbraApiEc.CollectionMissing;
|
||||
|
||||
return _tempMods.Unregister(tag, collection, priority) switch
|
||||
return _tempMods.Unregister(tag, collection, new ModPriority(priority)) switch
|
||||
{
|
||||
RedirectResult.Success => PenumbraApiEc.Success,
|
||||
RedirectResult.NotRegistered => PenumbraApiEc.NothingChanged,
|
||||
|
|
@ -1089,7 +1097,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
|
||||
public IReadOnlyDictionary<string, string[]>?[] GetGameObjectResourcePaths(ushort[] gameObjects)
|
||||
{
|
||||
var characters = gameObjects.Select(index => _objects.GetDalamudObject((int) index)).OfType<Character>();
|
||||
var characters = gameObjects.Select(index => _objects.GetDalamudObject((int)index)).OfType<Character>();
|
||||
var resourceTrees = _resourceTreeFactory.FromCharacters(characters, 0);
|
||||
var pathDictionaries = ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees);
|
||||
|
||||
|
|
@ -1153,7 +1161,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
var collection = _tempCollections.Collections.TryGetCollection(identifier, out var c)
|
||||
? c
|
||||
: _collectionManager.Active.Individual(identifier);
|
||||
var set = collection.MetaCache?.Manipulations.ToArray() ?? Array.Empty<MetaManipulation>();
|
||||
var set = collection.MetaCache?.Manipulations.ToArray() ?? [];
|
||||
return Functions.ToCompressedBase64(set, MetaManipulation.CurrentVersion);
|
||||
}
|
||||
|
||||
|
|
@ -1161,7 +1169,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
{
|
||||
CheckInitialized();
|
||||
AssociatedCollection(gameObjectIdx, out var collection);
|
||||
var set = collection.MetaCache?.Manipulations.ToArray() ?? Array.Empty<MetaManipulation>();
|
||||
var set = collection.MetaCache?.Manipulations.ToArray() ?? [];
|
||||
return Functions.ToCompressedBase64(set, MetaManipulation.CurrentVersion);
|
||||
}
|
||||
|
||||
|
|
@ -1190,7 +1198,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private unsafe ActorIdentifier AssociatedIdentifier(int gameObjectIdx)
|
||||
private ActorIdentifier AssociatedIdentifier(int gameObjectIdx)
|
||||
{
|
||||
if (gameObjectIdx < 0 || gameObjectIdx >= _objects.TotalCount)
|
||||
return ActorIdentifier.Invalid;
|
||||
|
|
@ -1217,10 +1225,9 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
CheckInitialized();
|
||||
try
|
||||
{
|
||||
if (Path.IsPathRooted(resolvedPath))
|
||||
return _lumina?.GetFileFromDisk<T>(resolvedPath);
|
||||
|
||||
return _gameData.GetFile<T>(resolvedPath);
|
||||
return Path.IsPathRooted(resolvedPath)
|
||||
? _lumina?.GetFileFromDisk<T>(resolvedPath)
|
||||
: _gameData.GetFile<T>(resolvedPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -1295,7 +1302,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
return _actors.CreatePlayer(b, worldId);
|
||||
}
|
||||
|
||||
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int _1, int _2, bool inherited)
|
||||
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting _1, int _2, bool inherited)
|
||||
=> ModSettingChanged?.Invoke(type, collection.Name, mod?.ModPath.Name ?? string.Empty, inherited);
|
||||
|
||||
private void OnCreatedCharacterBase(nint gameObject, ModCollection collection, nint drawObject)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Penumbra.Services;
|
|||
using Penumbra.String.Classes;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
|
||||
namespace Penumbra.Api;
|
||||
|
||||
|
|
@ -21,8 +22,8 @@ public class TempModManager : IDisposable
|
|||
{
|
||||
private readonly CommunicatorService _communicator;
|
||||
|
||||
private readonly Dictionary<ModCollection, List<TemporaryMod>> _mods = new();
|
||||
private readonly List<TemporaryMod> _modsForAllCollections = new();
|
||||
private readonly Dictionary<ModCollection, List<TemporaryMod>> _mods = [];
|
||||
private readonly List<TemporaryMod> _modsForAllCollections = [];
|
||||
|
||||
public TempModManager(CommunicatorService communicator)
|
||||
{
|
||||
|
|
@ -42,7 +43,7 @@ public class TempModManager : IDisposable
|
|||
=> _modsForAllCollections;
|
||||
|
||||
public RedirectResult Register(string tag, ModCollection? collection, Dictionary<Utf8GamePath, FullPath> dict,
|
||||
HashSet<MetaManipulation> manips, int priority)
|
||||
HashSet<MetaManipulation> manips, ModPriority priority)
|
||||
{
|
||||
var mod = GetOrCreateMod(tag, collection, priority, out var created);
|
||||
Penumbra.Log.Verbose($"{(created ? "Created" : "Changed")} temporary Mod {mod.Name}.");
|
||||
|
|
@ -51,10 +52,10 @@ public class TempModManager : IDisposable
|
|||
return RedirectResult.Success;
|
||||
}
|
||||
|
||||
public RedirectResult Unregister(string tag, ModCollection? collection, int? priority)
|
||||
public RedirectResult Unregister(string tag, ModCollection? collection, ModPriority? priority)
|
||||
{
|
||||
Penumbra.Log.Verbose($"Removing temporary mod with tag {tag}...");
|
||||
var list = collection == null ? _modsForAllCollections : _mods.TryGetValue(collection, out var l) ? l : null;
|
||||
var list = collection == null ? _modsForAllCollections : _mods.GetValueOrDefault(collection);
|
||||
if (list == null)
|
||||
return RedirectResult.NotRegistered;
|
||||
|
||||
|
|
@ -85,13 +86,13 @@ public class TempModManager : IDisposable
|
|||
{
|
||||
Penumbra.Log.Verbose($"Removing temporary Mod {mod.Name} from {collection.AnonymizedName}.");
|
||||
collection.Remove(mod);
|
||||
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, 0, 0, false);
|
||||
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.False, 0, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Penumbra.Log.Verbose($"Adding {(created ? "new " : string.Empty)}temporary Mod {mod.Name} to {collection.AnonymizedName}.");
|
||||
collection.Apply(mod, created);
|
||||
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, 1, 0, false);
|
||||
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.True, 0, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -116,7 +117,7 @@ public class TempModManager : IDisposable
|
|||
|
||||
// Find or create a mod with the given tag as name and the given priority, for the given collection (or all collections).
|
||||
// Returns the found or created mod and whether it was newly created.
|
||||
private TemporaryMod GetOrCreateMod(string tag, ModCollection? collection, int priority, out bool created)
|
||||
private TemporaryMod GetOrCreateMod(string tag, ModCollection? collection, ModPriority priority, out bool created)
|
||||
{
|
||||
List<TemporaryMod> list;
|
||||
if (collection == null)
|
||||
|
|
@ -129,14 +130,14 @@ public class TempModManager : IDisposable
|
|||
}
|
||||
else
|
||||
{
|
||||
list = new List<TemporaryMod>();
|
||||
list = [];
|
||||
_mods.Add(collection, list);
|
||||
}
|
||||
|
||||
var mod = list.Find(m => m.Priority == priority && m.Name == tag);
|
||||
if (mod == null)
|
||||
{
|
||||
mod = new TemporaryMod()
|
||||
mod = new TemporaryMod
|
||||
{
|
||||
Name = tag,
|
||||
Priority = priority,
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ public sealed class CollectionCache : IDisposable
|
|||
if (settings is not { Enabled: true })
|
||||
return;
|
||||
|
||||
foreach (var (group, groupIndex) in mod.Groups.WithIndex().OrderByDescending(g => g.Item1.Priority))
|
||||
foreach (var (group, groupIndex) in mod.Groups.WithIndex().OrderByDescending(g => g.Value.Priority))
|
||||
{
|
||||
if (group.Count == 0)
|
||||
continue;
|
||||
|
|
@ -246,13 +246,13 @@ public sealed class CollectionCache : IDisposable
|
|||
switch (group.Type)
|
||||
{
|
||||
case GroupType.Single:
|
||||
AddSubMod(group[(int)config], mod);
|
||||
AddSubMod(group[config.AsIndex], mod);
|
||||
break;
|
||||
case GroupType.Multi:
|
||||
{
|
||||
foreach (var (option, _) in group.WithIndex()
|
||||
.Where(p => ((1 << p.Item2) & config) != 0)
|
||||
.OrderByDescending(p => group.OptionPriority(p.Item2)))
|
||||
.Where(p => config.HasFlag(p.Index))
|
||||
.OrderByDescending(p => group.OptionPriority(p.Index)))
|
||||
AddSubMod(option, mod);
|
||||
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Penumbra.Interop.ResourceLoading;
|
|||
using Penumbra.Meta;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
|
|
@ -288,7 +289,7 @@ public class CollectionCacheManager : IDisposable
|
|||
MetaFileManager.CharacterUtility.LoadingFinished -= IncrementCounters;
|
||||
}
|
||||
|
||||
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool _)
|
||||
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx, bool _)
|
||||
{
|
||||
if (!collection.HasCache)
|
||||
return;
|
||||
|
|
@ -300,9 +301,9 @@ public class CollectionCacheManager : IDisposable
|
|||
cache.ReloadMod(mod!, true);
|
||||
break;
|
||||
case ModSettingChange.EnableState:
|
||||
if (oldValue == 0)
|
||||
if (oldValue == Setting.False)
|
||||
cache.AddMod(mod!, true);
|
||||
else if (oldValue == 1)
|
||||
else if (oldValue == Setting.True)
|
||||
cache.RemoveMod(mod!, true);
|
||||
else if (collection[mod!.Index].Settings?.Enabled == true)
|
||||
cache.ReloadMod(mod!, true);
|
||||
|
|
|
|||
|
|
@ -7,26 +7,15 @@ using Penumbra.Services;
|
|||
|
||||
namespace Penumbra.Collections.Manager;
|
||||
|
||||
public class CollectionEditor
|
||||
public class CollectionEditor(SaveService saveService, CommunicatorService communicator, ModStorage modStorage)
|
||||
{
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly SaveService _saveService;
|
||||
private readonly ModStorage _modStorage;
|
||||
|
||||
public CollectionEditor(SaveService saveService, CommunicatorService communicator, ModStorage modStorage)
|
||||
{
|
||||
_saveService = saveService;
|
||||
_communicator = communicator;
|
||||
_modStorage = modStorage;
|
||||
}
|
||||
|
||||
/// <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);
|
||||
InvokeChange(collection, ModSettingChange.Inheritance, mod, inherit ? Setting.False : Setting.True, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +31,8 @@ public class CollectionEditor
|
|||
|
||||
var inheritance = FixInheritance(collection, mod, false);
|
||||
((List<ModSettings?>)collection.Settings)[mod.Index]!.Enabled = newValue;
|
||||
InvokeChange(collection, ModSettingChange.EnableState, mod, inheritance ? -1 : newValue ? 0 : 1, 0);
|
||||
InvokeChange(collection, ModSettingChange.EnableState, mod, inheritance ? Setting.Indefinite : newValue ? Setting.False : Setting.True,
|
||||
0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +42,7 @@ public class CollectionEditor
|
|||
if (!mods.Aggregate(false, (current, mod) => current | FixInheritance(collection, mod, inherit)))
|
||||
return;
|
||||
|
||||
InvokeChange(collection, ModSettingChange.MultiInheritance, null, -1, 0);
|
||||
InvokeChange(collection, ModSettingChange.MultiInheritance, null, Setting.Indefinite, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -76,22 +66,22 @@ public class CollectionEditor
|
|||
if (!changes)
|
||||
return;
|
||||
|
||||
InvokeChange(collection, ModSettingChange.MultiEnableState, null, -1, 0);
|
||||
InvokeChange(collection, ModSettingChange.MultiEnableState, null, Setting.Indefinite, 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)
|
||||
public bool SetModPriority(ModCollection collection, Mod mod, ModPriority newValue)
|
||||
{
|
||||
var oldValue = collection.Settings[mod.Index]?.Priority ?? collection[mod.Index].Settings?.Priority ?? 0;
|
||||
var oldValue = collection.Settings[mod.Index]?.Priority ?? collection[mod.Index].Settings?.Priority ?? ModPriority.Default;
|
||||
if (newValue == oldValue)
|
||||
return false;
|
||||
|
||||
var inheritance = FixInheritance(collection, mod, false);
|
||||
((List<ModSettings?>)collection.Settings)[mod.Index]!.Priority = newValue;
|
||||
InvokeChange(collection, ModSettingChange.Priority, mod, inheritance ? -1 : oldValue, 0);
|
||||
InvokeChange(collection, ModSettingChange.Priority, mod, inheritance ? Setting.Indefinite : oldValue.AsSetting, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +89,7 @@ public class CollectionEditor
|
|||
/// 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)
|
||||
public bool SetModSetting(ModCollection collection, Mod mod, int groupIdx, Setting newValue)
|
||||
{
|
||||
var settings = collection.Settings[mod.Index] != null
|
||||
? collection.Settings[mod.Index]!.Settings
|
||||
|
|
@ -110,7 +100,7 @@ public class CollectionEditor
|
|||
|
||||
var inheritance = FixInheritance(collection, mod, false);
|
||||
((List<ModSettings?>)collection.Settings)[mod.Index]!.SetValue(mod, groupIdx, newValue);
|
||||
InvokeChange(collection, ModSettingChange.Setting, mod, inheritance ? -1 : (int)oldValue, groupIdx);
|
||||
InvokeChange(collection, ModSettingChange.Setting, mod, inheritance ? Setting.Indefinite : oldValue, groupIdx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -158,35 +148,17 @@ public class CollectionEditor
|
|||
if (savedSettings != null)
|
||||
{
|
||||
((Dictionary<string, ModSettings.SavedSettings>)collection.UnusedSettings)[targetName] = savedSettings.Value;
|
||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
|
||||
saveService.QueueSave(new ModCollectionSave(modStorage, collection));
|
||||
}
|
||||
else if (((Dictionary<string, ModSettings.SavedSettings>)collection.UnusedSettings).Remove(targetName))
|
||||
{
|
||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
|
||||
saveService.QueueSave(new ModCollectionSave(modStorage, collection));
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
@ -204,16 +176,16 @@ public class CollectionEditor
|
|||
|
||||
/// <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)
|
||||
private void InvokeChange(ModCollection changedCollection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx)
|
||||
{
|
||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, changedCollection));
|
||||
_communicator.ModSettingChanged.Invoke(changedCollection, type, mod, oldValue, groupIdx, false);
|
||||
saveService.QueueSave(new ModCollectionSave(modStorage, 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)
|
||||
private void RecurseInheritors(ModCollection directParent, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx)
|
||||
{
|
||||
foreach (var directInheritor in directParent.DirectParentOf)
|
||||
{
|
||||
|
|
@ -221,11 +193,11 @@ public class CollectionEditor
|
|||
{
|
||||
case ModSettingChange.MultiInheritance:
|
||||
case ModSettingChange.MultiEnableState:
|
||||
_communicator.ModSettingChanged.Invoke(directInheritor, type, null, oldValue, groupIdx, true);
|
||||
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);
|
||||
communicator.ModSettingChanged.Invoke(directInheritor, type, mod, oldValue, groupIdx, true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
|
|||
case ModPathChangeType.Reloaded:
|
||||
foreach (var collection in this)
|
||||
{
|
||||
if (collection.Settings[mod.Index]?.FixAllSettings(mod) ?? false)
|
||||
if (collection.Settings[mod.Index]?.Settings.FixAll(mod) ?? false)
|
||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ internal static class ModCollectionMigration
|
|||
|
||||
/// <summary> We treat every completely defaulted setting as inheritance-ready. </summary>
|
||||
private static bool SettingIsDefaultV0(ModSettings.SavedSettings setting)
|
||||
=> setting is { Enabled: false, Priority: 0 } && setting.Settings.Values.All(s => s == 0);
|
||||
=> setting is { Enabled: true, Priority.IsDefault: true } && setting.Settings.Values.All(s => s == Setting.Zero);
|
||||
|
||||
/// <inheritdoc cref="SettingIsDefaultV0(ModSettings.SavedSettings)"/>
|
||||
private static bool SettingIsDefaultV0(ModSettings? setting)
|
||||
=> setting is { Enabled: false, Priority: 0 } && setting.Settings.All(s => s == 0);
|
||||
=> setting is { Enabled: true, Priority.IsDefault: true } && setting.Settings.All(s => s == Setting.Zero);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Penumbra.Api;
|
|||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
|
||||
namespace Penumbra.Communication;
|
||||
|
||||
|
|
@ -12,13 +13,13 @@ namespace Penumbra.Communication;
|
|||
/// <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 old value of the setting before the change as Setting. </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<ModCollection, ModSettingChange, Mod?, int, int, bool, ModSettingChanged.Priority>(nameof(ModSettingChanged))
|
||||
: EventWrapper<ModCollection, ModSettingChange, Mod?, Setting, int, bool, ModSettingChanged.Priority>(nameof(ModSettingChanged))
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ public partial class TexToolsImporter
|
|||
?? new DirectoryInfo(Path.Combine(_currentModDirectory.FullName,
|
||||
numGroups == 1 ? $"Group {groupPriority + 1}" : $"Group {groupPriority + 1}, Part {groupId + 1}"));
|
||||
|
||||
uint? defaultSettings = group.SelectionType == GroupType.Multi ? 0u : null;
|
||||
Setting? defaultSettings = group.SelectionType == GroupType.Multi ? Setting.Zero : null;
|
||||
for (var i = 0; i + optionIdx < allOptions.Count && i < maxOptions; ++i)
|
||||
{
|
||||
var option = allOptions[i + optionIdx];
|
||||
|
|
@ -186,8 +186,8 @@ public partial class TexToolsImporter
|
|||
options.Add(_modManager.Creator.CreateSubMod(_currentModDirectory, optionFolder, option));
|
||||
if (option.IsChecked)
|
||||
defaultSettings = group.SelectionType == GroupType.Multi
|
||||
? defaultSettings!.Value | (1u << i)
|
||||
: (uint)i;
|
||||
? defaultSettings!.Value | Setting.Multi(i)
|
||||
: Setting.Single(i);
|
||||
|
||||
++_currentOptionIdx;
|
||||
}
|
||||
|
|
@ -205,12 +205,12 @@ public partial class TexToolsImporter
|
|||
_currentOptionName = option.Name;
|
||||
options.Insert(idx, ModCreator.CreateEmptySubMod(option.Name));
|
||||
if (option.IsChecked)
|
||||
defaultSettings = (uint) idx;
|
||||
defaultSettings = Setting.Single(idx);
|
||||
}
|
||||
}
|
||||
|
||||
_modManager.Creator.CreateOptionGroup(_currentModDirectory, group.SelectionType, name, groupPriority, groupPriority,
|
||||
defaultSettings ?? 0, group.Description, options);
|
||||
defaultSettings ?? Setting.Zero, group.Description, options);
|
||||
++groupPriority;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ public sealed unsafe class CollectionResolver(
|
|||
PerformanceTracker performance,
|
||||
IdentifiedCollectionCache cache,
|
||||
IClientState clientState,
|
||||
ObjectManager objects,
|
||||
IGameGui gameGui,
|
||||
ActorManager actors,
|
||||
CutsceneService cutscenes,
|
||||
|
|
@ -35,8 +36,8 @@ public sealed unsafe class CollectionResolver(
|
|||
public ModCollection PlayerCollection()
|
||||
{
|
||||
using var performance1 = performance.Measure(PerformanceType.IdentifyCollection);
|
||||
var gameObject = (GameObject*)(clientState.LocalPlayer?.Address ?? nint.Zero);
|
||||
if (gameObject == null)
|
||||
var gameObject = objects[0];
|
||||
if (!gameObject.Valid)
|
||||
return collectionManager.Active.ByType(CollectionType.Yourself)
|
||||
?? collectionManager.Active.Default;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ public interface IMod
|
|||
{
|
||||
LowerString Name { get; }
|
||||
|
||||
public int Index { get; }
|
||||
public int Priority { get; }
|
||||
public int Index { get; }
|
||||
public ModPriority Priority { get; }
|
||||
|
||||
public ISubMod Default { get; }
|
||||
public IReadOnlyList<IModGroup> Groups { get; }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Security.AccessControl;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
|
|
@ -33,6 +34,8 @@ public enum ModOptionChangeType
|
|||
|
||||
public class ModOptionEditor(CommunicatorService communicator, SaveService saveService, Configuration config)
|
||||
{
|
||||
|
||||
|
||||
/// <summary> Change the type of a group given by mod and index to type, if possible. </summary>
|
||||
public void ChangeModGroupType(Mod mod, int groupIdx, GroupType type)
|
||||
{
|
||||
|
|
@ -46,7 +49,7 @@ public class ModOptionEditor(CommunicatorService communicator, SaveService saveS
|
|||
}
|
||||
|
||||
/// <summary> Change the settings stored as default options in a mod.</summary>
|
||||
public void ChangeModGroupDefaultOption(Mod mod, int groupIdx, uint defaultOption)
|
||||
public void ChangeModGroupDefaultOption(Mod mod, int groupIdx, Setting defaultOption)
|
||||
{
|
||||
var group = mod.Groups[groupIdx];
|
||||
if (group.DefaultSettings == defaultOption)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public sealed class Mod : IMod
|
|||
{
|
||||
Name = "Forced Files",
|
||||
Index = -1,
|
||||
Priority = int.MaxValue,
|
||||
Priority = ModPriority.MaxValue,
|
||||
};
|
||||
|
||||
// Main Data
|
||||
|
|
@ -26,9 +26,9 @@ public sealed class Mod : IMod
|
|||
public bool IsTemporary
|
||||
=> Index < 0;
|
||||
|
||||
/// <summary>Unused if Index < 0 but used for special temporary mods.</summary>
|
||||
public int Priority
|
||||
=> 0;
|
||||
/// <summary>Unused if Index is less than 0 but used for special temporary mods.</summary>
|
||||
public ModPriority Priority
|
||||
=> ModPriority.Default;
|
||||
|
||||
internal Mod(DirectoryInfo modPath)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ public partial class ModCreator(SaveService _saveService, Configuration config,
|
|||
|
||||
/// <summary> Create a file for an option group from given data. </summary>
|
||||
public void CreateOptionGroup(DirectoryInfo baseFolder, GroupType type, string name,
|
||||
int priority, int index, uint defaultSettings, string desc, IEnumerable<ISubMod> subMods)
|
||||
int priority, int index, Setting defaultSettings, string desc, IEnumerable<ISubMod> subMods)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public interface IModGroup : IEnumerable<ISubMod>
|
|||
public string Description { get; }
|
||||
public GroupType Type { get; }
|
||||
public int Priority { get; }
|
||||
public uint DefaultSettings { get; set; }
|
||||
public Setting DefaultSettings { get; set; }
|
||||
|
||||
public int OptionPriority(Index optionIdx);
|
||||
|
||||
|
|
@ -31,6 +31,9 @@ public interface IModGroup : IEnumerable<ISubMod>
|
|||
public IModGroup Convert(GroupType type);
|
||||
public bool MoveOption(int optionIdxFrom, int optionIdxTo);
|
||||
public void UpdatePositions(int from = 0);
|
||||
|
||||
/// <summary> Ensure that a value is valid for a group. </summary>
|
||||
public Setting FixSetting(Setting setting);
|
||||
}
|
||||
|
||||
public readonly struct ModSaveGroup : ISavable
|
||||
|
|
@ -87,7 +90,7 @@ public readonly struct ModSaveGroup : ISavable
|
|||
j.WritePropertyName(nameof(Type));
|
||||
j.WriteValue(_group.Type.ToString());
|
||||
j.WritePropertyName(nameof(_group.DefaultSettings));
|
||||
j.WriteValue(_group.DefaultSettings);
|
||||
j.WriteValue(_group.DefaultSettings.Value);
|
||||
j.WritePropertyName("Options");
|
||||
j.WriteStartArray();
|
||||
for (var idx = 0; idx < _group.Count; ++idx)
|
||||
|
|
|
|||
61
Penumbra/Mods/Subclasses/ModPriority.cs
Normal file
61
Penumbra/Mods/Subclasses/ModPriority.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace Penumbra.Mods.Subclasses;
|
||||
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public readonly record struct ModPriority(int Value) :
|
||||
IComparisonOperators<ModPriority, ModPriority, bool>,
|
||||
IAdditionOperators<ModPriority, ModPriority, ModPriority>,
|
||||
IAdditionOperators<ModPriority, int, ModPriority>,
|
||||
ISubtractionOperators<ModPriority, ModPriority, ModPriority>,
|
||||
ISubtractionOperators<ModPriority, int, ModPriority>
|
||||
{
|
||||
public static readonly ModPriority Default = new(0);
|
||||
public static readonly ModPriority MaxValue = new(int.MaxValue);
|
||||
|
||||
public bool IsDefault
|
||||
=> Value == Default.Value;
|
||||
|
||||
public Setting AsSetting
|
||||
=> new((uint)Value);
|
||||
|
||||
public ModPriority Max(ModPriority other)
|
||||
=> this < other ? other : this;
|
||||
|
||||
public override string ToString()
|
||||
=> Value.ToString();
|
||||
|
||||
private class Converter : JsonConverter<ModPriority>
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, ModPriority value, JsonSerializer serializer)
|
||||
=> serializer.Serialize(writer, value.Value);
|
||||
|
||||
public override ModPriority ReadJson(JsonReader reader, Type objectType, ModPriority existingValue, bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
=> new(serializer.Deserialize<int>(reader));
|
||||
}
|
||||
|
||||
public static bool operator >(ModPriority left, ModPriority right)
|
||||
=> left.Value > right.Value;
|
||||
|
||||
public static bool operator >=(ModPriority left, ModPriority right)
|
||||
=> left.Value >= right.Value;
|
||||
|
||||
public static bool operator <(ModPriority left, ModPriority right)
|
||||
=> left.Value < right.Value;
|
||||
|
||||
public static bool operator <=(ModPriority left, ModPriority right)
|
||||
=> left.Value <= right.Value;
|
||||
|
||||
public static ModPriority operator +(ModPriority left, ModPriority right)
|
||||
=> new(left.Value + right.Value);
|
||||
|
||||
public static ModPriority operator +(ModPriority left, int right)
|
||||
=> new(left.Value + right);
|
||||
|
||||
public static ModPriority operator -(ModPriority left, ModPriority right)
|
||||
=> new(left.Value - right.Value);
|
||||
|
||||
public static ModPriority operator -(ModPriority left, int right)
|
||||
=> new(left.Value - right);
|
||||
}
|
||||
|
|
@ -11,8 +11,8 @@ namespace Penumbra.Mods.Subclasses;
|
|||
public class ModSettings
|
||||
{
|
||||
public static readonly ModSettings Empty = new();
|
||||
public List<uint> Settings { get; private init; } = [];
|
||||
public int Priority { get; set; }
|
||||
public SettingList Settings { get; private init; } = [];
|
||||
public ModPriority Priority { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
// Create an independent copy of the current settings.
|
||||
|
|
@ -21,7 +21,7 @@ public class ModSettings
|
|||
{
|
||||
Enabled = Enabled,
|
||||
Priority = Priority,
|
||||
Settings = [.. Settings],
|
||||
Settings = Settings.Clone(),
|
||||
};
|
||||
|
||||
// Create default settings for a given mod.
|
||||
|
|
@ -29,8 +29,8 @@ public class ModSettings
|
|||
=> new()
|
||||
{
|
||||
Enabled = false,
|
||||
Priority = 0,
|
||||
Settings = mod.Groups.Select(g => g.DefaultSettings).ToList(),
|
||||
Priority = ModPriority.Default,
|
||||
Settings = SettingList.Default(mod),
|
||||
};
|
||||
|
||||
// Return everything required to resolve things for a single mod with given settings (which can be null, in which case the default is used.
|
||||
|
|
@ -39,7 +39,7 @@ public class ModSettings
|
|||
if (settings == null)
|
||||
settings = DefaultSettings(mod);
|
||||
else
|
||||
settings.AddMissingSettings(mod);
|
||||
settings.Settings.FixSize(mod);
|
||||
|
||||
var dict = new Dictionary<Utf8GamePath, FullPath>();
|
||||
var set = new HashSet<MetaManipulation>();
|
||||
|
|
@ -49,13 +49,13 @@ public class ModSettings
|
|||
if (group.Type is GroupType.Single)
|
||||
{
|
||||
if (group.Count > 0)
|
||||
AddOption(group[(int)settings.Settings[index]]);
|
||||
AddOption(group[settings.Settings[index].AsIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (option, optionIdx) in group.WithIndex().OrderByDescending(o => group.OptionPriority(o.Index)))
|
||||
{
|
||||
if (((settings.Settings[index] >> optionIdx) & 1) == 1)
|
||||
if (settings.Settings[index].HasFlag(optionIdx))
|
||||
AddOption(option);
|
||||
}
|
||||
}
|
||||
|
|
@ -97,8 +97,8 @@ public class ModSettings
|
|||
var config = Settings[groupIdx];
|
||||
Settings[groupIdx] = group.Type switch
|
||||
{
|
||||
GroupType.Single => (uint)Math.Max(Math.Min(group.Count - 1, BitOperations.TrailingZeroCount(config)), 0),
|
||||
GroupType.Multi => 1u << (int)config,
|
||||
GroupType.Single => config.TurnMulti(group.Count),
|
||||
GroupType.Multi => Setting.Multi((int)config.Value),
|
||||
_ => config,
|
||||
};
|
||||
return config != Settings[groupIdx];
|
||||
|
|
@ -111,9 +111,11 @@ public class ModSettings
|
|||
var config = Settings[groupIdx];
|
||||
Settings[groupIdx] = group.Type switch
|
||||
{
|
||||
GroupType.Single => config >= optionIdx ? config > 1 ? config - 1 : 0 : config,
|
||||
GroupType.Multi => Functions.RemoveBit(config, optionIdx),
|
||||
_ => config,
|
||||
GroupType.Single => config.AsIndex >= optionIdx
|
||||
? config.AsIndex > 1 ? Setting.Single(config.AsIndex - 1) : Setting.Zero
|
||||
: config,
|
||||
GroupType.Multi => config.RemoveBit(optionIdx),
|
||||
_ => config,
|
||||
};
|
||||
return config != Settings[groupIdx];
|
||||
}
|
||||
|
|
@ -128,8 +130,8 @@ public class ModSettings
|
|||
var config = Settings[groupIdx];
|
||||
Settings[groupIdx] = group.Type switch
|
||||
{
|
||||
GroupType.Single => config == optionIdx ? (uint)movedToIdx : config,
|
||||
GroupType.Multi => Functions.MoveBit(config, optionIdx, movedToIdx),
|
||||
GroupType.Single => config.AsIndex == optionIdx ? Setting.Single(movedToIdx) : config,
|
||||
GroupType.Multi => config.MoveBit(optionIdx, movedToIdx),
|
||||
_ => config,
|
||||
};
|
||||
return config != Settings[groupIdx];
|
||||
|
|
@ -138,96 +140,52 @@ public class ModSettings
|
|||
}
|
||||
}
|
||||
|
||||
public bool FixAllSettings(Mod mod)
|
||||
/// <summary> Set a setting. Ensures that there are enough settings and fixes the setting beforehand. </summary>
|
||||
public void SetValue(Mod mod, int groupIdx, Setting newValue)
|
||||
{
|
||||
var ret = false;
|
||||
for (var i = 0; i < Settings.Count; ++i)
|
||||
{
|
||||
var newValue = FixSetting(mod.Groups[i], Settings[i]);
|
||||
if (newValue != Settings[i])
|
||||
{
|
||||
ret = true;
|
||||
Settings[i] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
return AddMissingSettings(mod) || ret;
|
||||
}
|
||||
|
||||
// Ensure that a value is valid for a group.
|
||||
private static uint FixSetting(IModGroup group, uint value)
|
||||
=> group.Type switch
|
||||
{
|
||||
GroupType.Single => (uint)Math.Min(value, group.Count - 1),
|
||||
GroupType.Multi => (uint)(value & ((1ul << group.Count) - 1)),
|
||||
_ => value,
|
||||
};
|
||||
|
||||
// Set a setting. Ensures that there are enough settings and fixes the setting beforehand.
|
||||
public void SetValue(Mod mod, int groupIdx, uint newValue)
|
||||
{
|
||||
AddMissingSettings(mod);
|
||||
Settings.FixSize(mod);
|
||||
var group = mod.Groups[groupIdx];
|
||||
Settings[groupIdx] = FixSetting(group, newValue);
|
||||
}
|
||||
|
||||
// Add defaulted settings up to the required count.
|
||||
private bool AddMissingSettings(Mod mod)
|
||||
{
|
||||
var changes = false;
|
||||
for (var i = Settings.Count; i < mod.Groups.Count; ++i)
|
||||
{
|
||||
Settings.Add(mod.Groups[i].DefaultSettings);
|
||||
changes = true;
|
||||
}
|
||||
|
||||
return changes;
|
||||
Settings[groupIdx] = group.FixSetting(newValue);
|
||||
}
|
||||
|
||||
// A simple struct conversion to easily save settings by name instead of value.
|
||||
public struct SavedSettings
|
||||
{
|
||||
public Dictionary<string, long> Settings;
|
||||
public int Priority;
|
||||
public bool Enabled;
|
||||
public Dictionary<string, Setting> Settings;
|
||||
public ModPriority Priority;
|
||||
public bool Enabled;
|
||||
|
||||
public SavedSettings DeepCopy()
|
||||
=> new()
|
||||
{
|
||||
Enabled = Enabled,
|
||||
Priority = Priority,
|
||||
Settings = Settings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||
};
|
||||
=> this with { Settings = Settings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) };
|
||||
|
||||
public SavedSettings(ModSettings settings, Mod mod)
|
||||
{
|
||||
Priority = settings.Priority;
|
||||
Enabled = settings.Enabled;
|
||||
Settings = new Dictionary<string, long>(mod.Groups.Count);
|
||||
settings.AddMissingSettings(mod);
|
||||
Settings = new Dictionary<string, Setting>(mod.Groups.Count);
|
||||
settings.Settings.FixSize(mod);
|
||||
|
||||
foreach (var (group, setting) in mod.Groups.Zip(settings.Settings))
|
||||
Settings.Add(group.Name, setting);
|
||||
}
|
||||
|
||||
// Convert and fix.
|
||||
public bool ToSettings(Mod mod, out ModSettings settings)
|
||||
public readonly bool ToSettings(Mod mod, out ModSettings settings)
|
||||
{
|
||||
var list = new List<uint>(mod.Groups.Count);
|
||||
var list = new SettingList(mod.Groups.Count);
|
||||
var changes = Settings.Count != mod.Groups.Count;
|
||||
foreach (var group in mod.Groups)
|
||||
{
|
||||
if (Settings.TryGetValue(group.Name, out var config))
|
||||
{
|
||||
var castConfig = (uint)Math.Clamp(config, 0, uint.MaxValue);
|
||||
var actualConfig = FixSetting(group, castConfig);
|
||||
var actualConfig = group.FixSetting(config);
|
||||
list.Add(actualConfig);
|
||||
if (actualConfig != config)
|
||||
changes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(0);
|
||||
list.Add(group.DefaultSettings);
|
||||
changes = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -245,7 +203,7 @@ public class ModSettings
|
|||
|
||||
// Return the settings for a given mod in a shareable format, using the names of groups and options instead of indices.
|
||||
// Does not repair settings but ignores settings not fitting to the given mod.
|
||||
public (bool Enabled, int Priority, Dictionary<string, IList<string>> Settings) ConvertToShareable(Mod mod)
|
||||
public (bool Enabled, ModPriority Priority, Dictionary<string, IList<string>> Settings) ConvertToShareable(Mod mod)
|
||||
{
|
||||
var dict = new Dictionary<string, IList<string>>(Settings.Count);
|
||||
foreach (var (setting, idx) in Settings.WithIndex())
|
||||
|
|
@ -254,16 +212,13 @@ public class ModSettings
|
|||
break;
|
||||
|
||||
var group = mod.Groups[idx];
|
||||
if (group.Type == GroupType.Single && setting < group.Count)
|
||||
if (group.Type == GroupType.Single && setting.Value < (ulong)group.Count)
|
||||
{
|
||||
dict.Add(group.Name, new[]
|
||||
{
|
||||
group[(int)setting].Name,
|
||||
});
|
||||
dict.Add(group.Name, [group[(int)setting.Value].Name]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var list = group.Where((_, optionIdx) => (setting & (1 << optionIdx)) != 0).Select(o => o.Name).ToList();
|
||||
var list = group.Where((_, optionIdx) => (setting.Value & (1ul << optionIdx)) != 0).Select(o => o.Name).ToList();
|
||||
dict.Add(group.Name, list);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ public sealed class MultiModGroup : IModGroup
|
|||
public GroupType Type
|
||||
=> GroupType.Multi;
|
||||
|
||||
public string Name { get; set; } = "Group";
|
||||
public string Description { get; set; } = "A non-exclusive group of settings.";
|
||||
public int Priority { get; set; }
|
||||
public uint DefaultSettings { get; set; }
|
||||
public string Name { get; set; } = "Group";
|
||||
public string Description { get; set; } = "A non-exclusive group of settings.";
|
||||
public int Priority { get; set; }
|
||||
public Setting DefaultSettings { get; set; }
|
||||
|
||||
public int OptionPriority(Index idx)
|
||||
=> PrioritizedOptions[idx].Priority;
|
||||
|
|
@ -44,7 +44,7 @@ public sealed class MultiModGroup : IModGroup
|
|||
Name = json[nameof(Name)]?.ToObject<string>() ?? string.Empty,
|
||||
Description = json[nameof(Description)]?.ToObject<string>() ?? string.Empty,
|
||||
Priority = json[nameof(Priority)]?.ToObject<int>() ?? 0,
|
||||
DefaultSettings = json[nameof(DefaultSettings)]?.ToObject<uint>() ?? 0,
|
||||
DefaultSettings = json[nameof(DefaultSettings)]?.ToObject<Setting>() ?? Setting.Zero,
|
||||
};
|
||||
if (ret.Name.Length == 0)
|
||||
return null;
|
||||
|
|
@ -56,7 +56,8 @@ public sealed class MultiModGroup : IModGroup
|
|||
if (ret.PrioritizedOptions.Count == IModGroup.MaxMultiOptions)
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Multi Group {ret.Name} in {mod.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options.", NotificationType.Warning);
|
||||
$"Multi Group {ret.Name} in {mod.Name} has more than {IModGroup.MaxMultiOptions} options, ignoring excessive options.",
|
||||
NotificationType.Warning);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ public sealed class MultiModGroup : IModGroup
|
|||
ret.PrioritizedOptions.Add((subMod, priority));
|
||||
}
|
||||
|
||||
ret.DefaultSettings = (uint)(ret.DefaultSettings & ((1ul << ret.Count) - 1));
|
||||
ret.DefaultSettings = ret.FixSetting(ret.DefaultSettings);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -82,7 +83,7 @@ public sealed class MultiModGroup : IModGroup
|
|||
Name = Name,
|
||||
Description = Description,
|
||||
Priority = Priority,
|
||||
DefaultSettings = (uint)Math.Max(Math.Min(Count - 1, BitOperations.TrailingZeroCount(DefaultSettings)), 0),
|
||||
DefaultSettings = DefaultSettings.TurnMulti(Count),
|
||||
};
|
||||
multi.OptionData.AddRange(PrioritizedOptions.Select(p => p.Mod));
|
||||
return multi;
|
||||
|
|
@ -95,7 +96,7 @@ public sealed class MultiModGroup : IModGroup
|
|||
if (!PrioritizedOptions.Move(optionIdxFrom, optionIdxTo))
|
||||
return false;
|
||||
|
||||
DefaultSettings = Functions.MoveBit(DefaultSettings, optionIdxFrom, optionIdxTo);
|
||||
DefaultSettings = DefaultSettings.MoveBit(optionIdxFrom, optionIdxTo);
|
||||
UpdatePositions(Math.Min(optionIdxFrom, optionIdxTo));
|
||||
return true;
|
||||
}
|
||||
|
|
@ -105,4 +106,7 @@ public sealed class MultiModGroup : IModGroup
|
|||
foreach (var ((o, _), i) in PrioritizedOptions.WithIndex().Skip(from))
|
||||
o.SetPosition(o.GroupIdx, i);
|
||||
}
|
||||
|
||||
public Setting FixSetting(Setting setting)
|
||||
=> new(setting.Value & ((1ul << Count) - 1));
|
||||
}
|
||||
|
|
|
|||
62
Penumbra/Mods/Subclasses/Setting.cs
Normal file
62
Penumbra/Mods/Subclasses/Setting.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using Newtonsoft.Json;
|
||||
using OtterGui;
|
||||
|
||||
namespace Penumbra.Mods.Subclasses;
|
||||
|
||||
[JsonConverter(typeof(Converter))]
|
||||
public readonly record struct Setting(ulong Value)
|
||||
{
|
||||
public static readonly Setting Zero = new(0);
|
||||
public static readonly Setting True = new(1);
|
||||
public static readonly Setting False = new(0);
|
||||
public static readonly Setting Indefinite = new(ulong.MaxValue);
|
||||
|
||||
public static Setting Multi(int idx)
|
||||
=> new(1ul << idx);
|
||||
|
||||
public static Setting Single(int idx)
|
||||
=> new(Math.Max(0ul, (ulong)idx));
|
||||
|
||||
public static Setting operator |(Setting lhs, Setting rhs)
|
||||
=> new(lhs.Value | rhs.Value);
|
||||
|
||||
public int AsIndex
|
||||
=> (int)Math.Clamp(Value, 0ul, int.MaxValue);
|
||||
|
||||
public bool HasFlag(int idx)
|
||||
=> idx >= 0 && (Value & (1ul << idx)) != 0;
|
||||
|
||||
public Setting MoveBit(int idx1, int idx2)
|
||||
=> new(Functions.MoveBit(Value, idx1, idx2));
|
||||
|
||||
public Setting RemoveBit(int idx)
|
||||
=> new(Functions.RemoveBit(Value, idx));
|
||||
|
||||
public Setting SetBit(int idx, bool value)
|
||||
=> new(value ? Value | (1ul << idx) : Value & ~(1ul << idx));
|
||||
|
||||
public static Setting AllBits(int count)
|
||||
=> new((1ul << Math.Clamp(count, 0, 63)) - 1);
|
||||
|
||||
public Setting TurnMulti(int count)
|
||||
=> new(Math.Max((ulong)Math.Min(count - 1, BitOperations.TrailingZeroCount(Value)), 0));
|
||||
|
||||
public ModPriority AsPriority
|
||||
=> new((int)(Value & 0xFFFFFFFF));
|
||||
|
||||
public static Setting FromBool(bool value)
|
||||
=> value ? True : False;
|
||||
|
||||
public bool AsBool
|
||||
=> Value != 0;
|
||||
|
||||
private class Converter : JsonConverter<Setting>
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, Setting value, JsonSerializer serializer)
|
||||
=> serializer.Serialize(writer, value.Value);
|
||||
|
||||
public override Setting ReadJson(JsonReader reader, Type objectType, Setting existingValue, bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
=> new(serializer.Deserialize<ulong>(reader));
|
||||
}
|
||||
}
|
||||
57
Penumbra/Mods/Subclasses/SettingList.cs
Normal file
57
Penumbra/Mods/Subclasses/SettingList.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
namespace Penumbra.Mods.Subclasses;
|
||||
|
||||
public class SettingList : List<Setting>
|
||||
{
|
||||
public SettingList()
|
||||
{ }
|
||||
|
||||
public SettingList(int capacity)
|
||||
: base(capacity)
|
||||
{ }
|
||||
|
||||
public SettingList(IEnumerable<Setting> settings)
|
||||
=> AddRange(settings);
|
||||
|
||||
public SettingList Clone()
|
||||
=> new(this);
|
||||
|
||||
public static SettingList Default(Mod mod)
|
||||
=> new(mod.Groups.Select(g => g.DefaultSettings));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public bool FixSize(Mod mod)
|
||||
{
|
||||
var diff = Count - mod.Groups.Count;
|
||||
|
||||
switch (diff)
|
||||
{
|
||||
case 0: return false;
|
||||
case > 0:
|
||||
RemoveRange(mod.Groups.Count, diff);
|
||||
return true;
|
||||
default:
|
||||
EnsureCapacity(mod.Groups.Count);
|
||||
for (var i = Count; i < mod.Groups.Count; ++i)
|
||||
Add(mod.Groups[i].DefaultSettings);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public bool FixAll(Mod mod)
|
||||
{
|
||||
var ret = false;
|
||||
for (var i = 0; i < Count; ++i)
|
||||
{
|
||||
var oldValue = this[i];
|
||||
var newValue = mod.Groups[i].FixSetting(oldValue);
|
||||
if (newValue == oldValue)
|
||||
continue;
|
||||
|
||||
ret = true;
|
||||
this[i] = newValue;
|
||||
}
|
||||
|
||||
return FixSize(mod) | ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,10 +12,10 @@ public sealed class SingleModGroup : IModGroup
|
|||
public GroupType Type
|
||||
=> GroupType.Single;
|
||||
|
||||
public string Name { get; set; } = "Option";
|
||||
public string Description { get; set; } = "A mutually exclusive group of settings.";
|
||||
public int Priority { get; set; }
|
||||
public uint DefaultSettings { get; set; }
|
||||
public string Name { get; set; } = "Option";
|
||||
public string Description { get; set; } = "A mutually exclusive group of settings.";
|
||||
public int Priority { get; set; }
|
||||
public Setting DefaultSettings { get; set; }
|
||||
|
||||
public readonly List<SubMod> OptionData = [];
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ public sealed class SingleModGroup : IModGroup
|
|||
Name = json[nameof(Name)]?.ToObject<string>() ?? string.Empty,
|
||||
Description = json[nameof(Description)]?.ToObject<string>() ?? string.Empty,
|
||||
Priority = json[nameof(Priority)]?.ToObject<int>() ?? 0,
|
||||
DefaultSettings = json[nameof(DefaultSettings)]?.ToObject<uint>() ?? 0u,
|
||||
DefaultSettings = json[nameof(DefaultSettings)]?.ToObject<Setting>() ?? Setting.Zero,
|
||||
};
|
||||
if (ret.Name.Length == 0)
|
||||
return null;
|
||||
|
|
@ -57,9 +57,7 @@ public sealed class SingleModGroup : IModGroup
|
|||
ret.OptionData.Add(subMod);
|
||||
}
|
||||
|
||||
if ((int)ret.DefaultSettings >= ret.Count)
|
||||
ret.DefaultSettings = 0;
|
||||
|
||||
ret.DefaultSettings = ret.FixSetting(ret.DefaultSettings);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +72,7 @@ public sealed class SingleModGroup : IModGroup
|
|||
Name = Name,
|
||||
Description = Description,
|
||||
Priority = Priority,
|
||||
DefaultSettings = 1u << (int)DefaultSettings,
|
||||
DefaultSettings = Setting.Multi((int) DefaultSettings.Value),
|
||||
};
|
||||
multi.PrioritizedOptions.AddRange(OptionData.Select((o, i) => (o, i)));
|
||||
return multi;
|
||||
|
|
@ -87,19 +85,20 @@ public sealed class SingleModGroup : IModGroup
|
|||
if (!OptionData.Move(optionIdxFrom, optionIdxTo))
|
||||
return false;
|
||||
|
||||
var currentIndex = DefaultSettings.AsIndex;
|
||||
// Update default settings with the move.
|
||||
if (DefaultSettings == optionIdxFrom)
|
||||
if (currentIndex == optionIdxFrom)
|
||||
{
|
||||
DefaultSettings = (uint)optionIdxTo;
|
||||
DefaultSettings = Setting.Single(optionIdxTo);
|
||||
}
|
||||
else if (optionIdxFrom < optionIdxTo)
|
||||
{
|
||||
if (DefaultSettings > optionIdxFrom && DefaultSettings <= optionIdxTo)
|
||||
--DefaultSettings;
|
||||
if (currentIndex > optionIdxFrom && currentIndex <= optionIdxTo)
|
||||
DefaultSettings = Setting.Single(currentIndex - 1);
|
||||
}
|
||||
else if (DefaultSettings < optionIdxFrom && DefaultSettings >= optionIdxTo)
|
||||
else if (currentIndex < optionIdxFrom && currentIndex >= optionIdxTo)
|
||||
{
|
||||
++DefaultSettings;
|
||||
DefaultSettings = Setting.Single(currentIndex + 1);
|
||||
}
|
||||
|
||||
UpdatePositions(Math.Min(optionIdxFrom, optionIdxTo));
|
||||
|
|
@ -111,4 +110,7 @@ public sealed class SingleModGroup : IModGroup
|
|||
foreach (var (o, i) in OptionData.WithIndex().Skip(from))
|
||||
o.SetPosition(o.GroupIdx, i);
|
||||
}
|
||||
|
||||
public Setting FixSetting(Setting setting)
|
||||
=> Count == 0 ? Setting.Zero : new Setting(Math.Min(setting.Value, (ulong)(Count - 1)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public class TemporaryMod : IMod
|
|||
{
|
||||
public LowerString Name { get; init; } = LowerString.Empty;
|
||||
public int Index { get; init; } = -2;
|
||||
public int Priority { get; init; } = int.MaxValue;
|
||||
public ModPriority Priority { get; init; } = ModPriority.MaxValue;
|
||||
|
||||
public int TotalManipulations
|
||||
=> Default.Manipulations.Count;
|
||||
|
|
@ -27,10 +27,7 @@ public class TemporaryMod : IMod
|
|||
=> Array.Empty<IModGroup>();
|
||||
|
||||
public IEnumerable<SubMod> AllSubMods
|
||||
=> new[]
|
||||
{
|
||||
Default,
|
||||
};
|
||||
=> [Default];
|
||||
|
||||
public TemporaryMod()
|
||||
=> Default = new SubMod(this);
|
||||
|
|
|
|||
|
|
@ -341,15 +341,15 @@ public class ConfigMigrationService(SaveService saveService) : IService
|
|||
var text = File.ReadAllText(collectionJson.FullName);
|
||||
var data = JArray.Parse(text);
|
||||
|
||||
var maxPriority = 0;
|
||||
var maxPriority = ModPriority.Default;
|
||||
var dict = new Dictionary<string, ModSettings.SavedSettings>();
|
||||
foreach (var setting in data.Cast<JObject>())
|
||||
{
|
||||
var modName = (string)setting["FolderName"]!;
|
||||
var enabled = (bool)setting["Enabled"]!;
|
||||
var priority = (int)setting["Priority"]!;
|
||||
var settings = setting["Settings"]!.ToObject<Dictionary<string, long>>()
|
||||
?? setting["Conf"]!.ToObject<Dictionary<string, long>>();
|
||||
var modName = setting["FolderName"]?.ToObject<string>()!;
|
||||
var enabled = setting["Enabled"]?.ToObject<bool>() ?? false;
|
||||
var priority = setting["Priority"]?.ToObject<ModPriority>() ?? ModPriority.Default;
|
||||
var settings = setting["Settings"]!.ToObject<Dictionary<string, Setting>>()
|
||||
?? setting["Conf"]!.ToObject<Dictionary<string, Setting>>();
|
||||
|
||||
dict[modName] = new ModSettings.SavedSettings()
|
||||
{
|
||||
|
|
@ -357,7 +357,7 @@ public class ConfigMigrationService(SaveService saveService) : IService
|
|||
Priority = priority,
|
||||
Settings = settings!,
|
||||
};
|
||||
maxPriority = Math.Max(maxPriority, priority);
|
||||
maxPriority = maxPriority.Max(priority);
|
||||
}
|
||||
|
||||
InvertModListOrder = _data[nameof(InvertModListOrder)]?.ToObject<bool>() ?? InvertModListOrder;
|
||||
|
|
@ -365,8 +365,7 @@ public class ConfigMigrationService(SaveService saveService) : IService
|
|||
dict = dict.ToDictionary(kvp => kvp.Key, kvp => kvp.Value with { Priority = maxPriority - kvp.Value.Priority });
|
||||
|
||||
var emptyStorage = new ModStorage();
|
||||
var collection = ModCollection.CreateFromData(saveService, emptyStorage, ModCollection.DefaultCollectionName, 0, 1, dict,
|
||||
Array.Empty<string>());
|
||||
var collection = ModCollection.CreateFromData(saveService, emptyStorage, ModCollection.DefaultCollectionName, 0, 1, dict, []);
|
||||
saveService.ImmediateSaveSync(new ModCollectionSave(emptyStorage, collection));
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
|||
|
|
@ -694,7 +694,7 @@ public class ItemSwapTab : IDisposable, ITab
|
|||
UpdateMod(_mod, _mod.Index < newCollection.Settings.Count ? newCollection[_mod.Index].Settings : null);
|
||||
}
|
||||
|
||||
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool inherited)
|
||||
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx, bool inherited)
|
||||
{
|
||||
if (collection != _collectionManager.Active.Current || mod != _mod)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
|
|||
// @formatter:on
|
||||
SetFilterTooltip();
|
||||
|
||||
SelectionChanged += OnSelectionChange;
|
||||
SelectionChanged += OnSelectionChange;
|
||||
if (_config.Ephemeral.LastModPath.Length > 0)
|
||||
{
|
||||
var mod = _modManager.FirstOrDefault(m
|
||||
|
|
@ -92,7 +92,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
|
|||
OnCollectionChange(CollectionType.Current, null, _collectionManager.Active.Current, "");
|
||||
}
|
||||
|
||||
private static readonly string[] ValidModExtensions =
|
||||
private static readonly string[] ValidModExtensions =
|
||||
[
|
||||
".ttmp",
|
||||
".ttmp2",
|
||||
|
|
@ -191,15 +191,15 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
|
|||
}
|
||||
}
|
||||
|
||||
if (state.Priority != 0 && !_config.HidePrioritiesInSelector)
|
||||
if (!state.Priority.IsDefault && !_config.HidePrioritiesInSelector)
|
||||
{
|
||||
var line = ImGui.GetItemRectMin().Y;
|
||||
var itemPos = ImGui.GetItemRectMax().X;
|
||||
var maxWidth = ImGui.GetWindowPos().X + ImGui.GetWindowContentRegionMax().X;
|
||||
var priorityString = $"[{state.Priority}]";
|
||||
var Size = ImGui.CalcTextSize(priorityString).X;
|
||||
var size = ImGui.CalcTextSize(priorityString).X;
|
||||
var remainingSpace = maxWidth - itemPos;
|
||||
var offset = remainingSpace - Size;
|
||||
var offset = remainingSpace - size;
|
||||
if (ImGui.GetScrollMaxY() == 0)
|
||||
offset -= ImGui.GetStyle().ItemInnerSpacing.X;
|
||||
|
||||
|
|
@ -427,7 +427,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
|
|||
|
||||
#region Automatic cache update functions.
|
||||
|
||||
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int oldValue, int groupIdx, bool inherited)
|
||||
private void OnSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx, bool inherited)
|
||||
{
|
||||
if (collection != _collectionManager.Active.Current)
|
||||
return;
|
||||
|
|
@ -517,8 +517,8 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
|
|||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct ModState
|
||||
{
|
||||
public ColorId Color;
|
||||
public int Priority;
|
||||
public ColorId Color;
|
||||
public ModPriority Priority;
|
||||
}
|
||||
|
||||
private const StringComparison IgnoreCase = StringComparison.OrdinalIgnoreCase;
|
||||
|
|
@ -744,7 +744,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector<Mod, ModFileSyste
|
|||
state = new ModState
|
||||
{
|
||||
Color = ColorId.EnabledMod,
|
||||
Priority = settings?.Priority ?? 0,
|
||||
Priority = settings?.Priority ?? ModPriority.Default,
|
||||
};
|
||||
if (ApplyStringFilters(leaf, mod))
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -9,38 +9,30 @@ using Penumbra.Collections.Manager;
|
|||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.Mods.Subclasses;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.UI.Classes;
|
||||
|
||||
namespace Penumbra.UI.ModsTab;
|
||||
|
||||
public class ModPanelConflictsTab : ITab
|
||||
public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSystemSelector selector) : ITab
|
||||
{
|
||||
private readonly ModFileSystemSelector _selector;
|
||||
private readonly CollectionManager _collectionManager;
|
||||
|
||||
public ModPanelConflictsTab(CollectionManager collectionManager, ModFileSystemSelector selector)
|
||||
{
|
||||
_collectionManager = collectionManager;
|
||||
_selector = selector;
|
||||
}
|
||||
|
||||
private int? _currentPriority = null;
|
||||
private int? _currentPriority;
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
=> "Conflicts"u8;
|
||||
|
||||
public bool IsVisible
|
||||
=> _collectionManager.Active.Current.Conflicts(_selector.Selected!).Count > 0;
|
||||
=> collectionManager.Active.Current.Conflicts(selector.Selected!).Count > 0;
|
||||
|
||||
private readonly ConditionalWeakTable<IMod, object> _expandedMods = new();
|
||||
private readonly ConditionalWeakTable<IMod, object> _expandedMods = [];
|
||||
|
||||
private int GetPriority(ModConflicts conflicts)
|
||||
private ModPriority GetPriority(ModConflicts conflicts)
|
||||
{
|
||||
if (conflicts.Mod2.Index < 0)
|
||||
return conflicts.Mod2.Priority;
|
||||
|
||||
return _collectionManager.Active.Current[conflicts.Mod2.Index].Settings?.Priority ?? 0;
|
||||
return collectionManager.Active.Current[conflicts.Mod2.Index].Settings?.Priority ?? ModPriority.Default;
|
||||
}
|
||||
|
||||
public void DrawContent()
|
||||
|
|
@ -63,8 +55,8 @@ public class ModPanelConflictsTab : ITab
|
|||
DrawCurrentRow(priorityWidth);
|
||||
|
||||
// Can not be null because otherwise the tab bar is never drawn.
|
||||
var mod = _selector.Selected!;
|
||||
foreach (var (conflict, index) in _collectionManager.Active.Current.Conflicts(mod).OrderByDescending(GetPriority)
|
||||
var mod = selector.Selected!;
|
||||
foreach (var (conflict, index) in collectionManager.Active.Current.Conflicts(mod).OrderByDescending(GetPriority)
|
||||
.ThenBy(c => c.Mod2.Name.Lower).WithIndex())
|
||||
{
|
||||
using var id = ImRaii.PushId(index);
|
||||
|
|
@ -77,18 +69,18 @@ public class ModPanelConflictsTab : ITab
|
|||
ImGui.TableNextColumn();
|
||||
using var c = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderLine.Value());
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(_selector.Selected!.Name);
|
||||
ImGui.TextUnformatted(selector.Selected!.Name);
|
||||
ImGui.TableNextColumn();
|
||||
var priority = _collectionManager.Active.Current[_selector.Selected!.Index].Settings!.Priority;
|
||||
var priority = collectionManager.Active.Current[selector.Selected!.Index].Settings!.Priority.Value;
|
||||
ImGui.SetNextItemWidth(priorityWidth);
|
||||
if (ImGui.InputInt("##priority", ref priority, 0, 0, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||
_currentPriority = priority;
|
||||
|
||||
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
|
||||
{
|
||||
if (_currentPriority != _collectionManager.Active.Current[_selector.Selected!.Index].Settings!.Priority)
|
||||
_collectionManager.Editor.SetModPriority(_collectionManager.Active.Current, (Mod)_selector.Selected!,
|
||||
_currentPriority.Value);
|
||||
if (_currentPriority != collectionManager.Active.Current[selector.Selected!.Index].Settings!.Priority.Value)
|
||||
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, selector.Selected!,
|
||||
new ModPriority(_currentPriority.Value));
|
||||
|
||||
_currentPriority = null;
|
||||
}
|
||||
|
|
@ -104,7 +96,7 @@ public class ModPanelConflictsTab : ITab
|
|||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
if (ImGui.Selectable(conflict.Mod2.Name) && conflict.Mod2 is Mod otherMod)
|
||||
_selector.SelectByValue(otherMod);
|
||||
selector.SelectByValue(otherMod);
|
||||
var hovered = ImGui.IsItemHovered();
|
||||
var rightClicked = ImGui.IsItemClicked(ImGuiMouseButton.Right);
|
||||
if (conflict.Mod2 is Mod otherMod2)
|
||||
|
|
@ -112,7 +104,7 @@ public class ModPanelConflictsTab : ITab
|
|||
if (hovered)
|
||||
ImGui.SetTooltip("Click to jump to mod, Control + Right-Click to disable mod.");
|
||||
if (rightClicked && ImGui.GetIO().KeyCtrl)
|
||||
_collectionManager.Editor.SetModState(_collectionManager.Active.Current, otherMod2, false);
|
||||
collectionManager.Editor.SetModState(collectionManager.Active.Current, otherMod2, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,7 +138,7 @@ public class ModPanelConflictsTab : ITab
|
|||
ImGui.TableNextColumn();
|
||||
var conflictPriority = DrawPriorityInput(conflict, priorityWidth);
|
||||
ImGui.SameLine();
|
||||
var selectedPriority = _collectionManager.Active.Current[_selector.Selected!.Index].Settings!.Priority;
|
||||
var selectedPriority = collectionManager.Active.Current[selector.Selected!.Index].Settings!.Priority.Value;
|
||||
DrawPriorityButtons(conflict.Mod2 as Mod, conflictPriority, selectedPriority, buttonSize);
|
||||
ImGui.TableNextColumn();
|
||||
DrawExpandButton(conflict.Mod2, expanded, buttonSize);
|
||||
|
|
@ -171,7 +163,7 @@ public class ModPanelConflictsTab : ITab
|
|||
using var color = ImRaii.PushColor(ImGuiCol.Text,
|
||||
conflict.HasPriority ? ColorId.HandledConflictMod.Value() : ColorId.ConflictingMod.Value());
|
||||
using var disabled = ImRaii.Disabled(conflict.Mod2.Index < 0);
|
||||
var priority = _currentPriority ?? GetPriority(conflict);
|
||||
var priority = _currentPriority ?? GetPriority(conflict).Value;
|
||||
|
||||
ImGui.SetNextItemWidth(priorityWidth);
|
||||
if (ImGui.InputInt("##priority", ref priority, 0, 0, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||
|
|
@ -179,8 +171,9 @@ public class ModPanelConflictsTab : ITab
|
|||
|
||||
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
|
||||
{
|
||||
if (_currentPriority != GetPriority(conflict))
|
||||
_collectionManager.Editor.SetModPriority(_collectionManager.Active.Current, (Mod)conflict.Mod2, _currentPriority.Value);
|
||||
if (_currentPriority != GetPriority(conflict).Value)
|
||||
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, (Mod)conflict.Mod2,
|
||||
new ModPriority(_currentPriority.Value));
|
||||
|
||||
_currentPriority = null;
|
||||
}
|
||||
|
|
@ -195,12 +188,14 @@ public class ModPanelConflictsTab : ITab
|
|||
private void DrawPriorityButtons(Mod? conflict, int conflictPriority, int selectedPriority, Vector2 buttonSize)
|
||||
{
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.SortNumericUpAlt.ToIconString(), buttonSize,
|
||||
$"Set the priority of the currently selected mod to this mods priority plus one. ({selectedPriority} -> {conflictPriority + 1})", selectedPriority > conflictPriority, true))
|
||||
_collectionManager.Editor.SetModPriority(_collectionManager.Active.Current, _selector.Selected!, conflictPriority + 1);
|
||||
$"Set the priority of the currently selected mod to this mods priority plus one. ({selectedPriority} -> {conflictPriority + 1})",
|
||||
selectedPriority > conflictPriority, true))
|
||||
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, selector.Selected!,
|
||||
new ModPriority(conflictPriority + 1));
|
||||
ImGui.SameLine();
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.SortNumericDownAlt.ToIconString(), buttonSize,
|
||||
$"Set the priority of this mod to the currently selected mods priority minus one. ({conflictPriority} -> {selectedPriority - 1})",
|
||||
selectedPriority > conflictPriority || conflict == null, true))
|
||||
_collectionManager.Editor.SetModPriority(_collectionManager.Active.Current, conflict!, selectedPriority - 1);
|
||||
collectionManager.Editor.SetModPriority(collectionManager.Active.Current, conflict!, new ModPriority(selectedPriority - 1));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -502,18 +502,16 @@ public class ModPanelEditTab(
|
|||
|
||||
if (group.Type == GroupType.Single)
|
||||
{
|
||||
if (ImGui.RadioButton("##default", group.DefaultSettings == optionIdx))
|
||||
panel._modManager.OptionEditor.ChangeModGroupDefaultOption(panel._mod, groupIdx, (uint)optionIdx);
|
||||
if (ImGui.RadioButton("##default", group.DefaultSettings.AsIndex == optionIdx))
|
||||
panel._modManager.OptionEditor.ChangeModGroupDefaultOption(panel._mod, groupIdx, Setting.Single(optionIdx));
|
||||
|
||||
ImGuiUtil.HoverTooltip($"Set {option.Name} as the default choice for this group.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var isDefaultOption = ((group.DefaultSettings >> optionIdx) & 1) != 0;
|
||||
var isDefaultOption = group.DefaultSettings.HasFlag(optionIdx);
|
||||
if (ImGui.Checkbox("##default", ref isDefaultOption))
|
||||
panel._modManager.OptionEditor.ChangeModGroupDefaultOption(panel._mod, groupIdx, isDefaultOption
|
||||
? group.DefaultSettings | (1u << optionIdx)
|
||||
: group.DefaultSettings & ~(1u << optionIdx));
|
||||
panel._modManager.OptionEditor.ChangeModGroupDefaultOption(panel._mod, groupIdx, group.DefaultSettings.SetBit(optionIdx, isDefaultOption));
|
||||
|
||||
ImGuiUtil.HoverTooltip($"{(isDefaultOption ? "Disable" : "Enable")} {option.Name} per default in this group.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class ModPanelSettingsTab : ITab
|
|||
private ModSettings _settings = null!;
|
||||
private ModCollection _collection = null!;
|
||||
private bool _empty;
|
||||
private int? _currentPriority = null;
|
||||
private int? _currentPriority;
|
||||
|
||||
public ModPanelSettingsTab(CollectionManager collectionManager, ModManager modManager, ModFileSystemSelector selector,
|
||||
TutorialService tutorial, CommunicatorService communicator, Configuration config)
|
||||
|
|
@ -136,15 +136,15 @@ public class ModPanelSettingsTab : ITab
|
|||
private void DrawPriorityInput()
|
||||
{
|
||||
using var group = ImRaii.Group();
|
||||
var priority = _currentPriority ?? _settings.Priority;
|
||||
var priority = _currentPriority ?? _settings.Priority.Value;
|
||||
ImGui.SetNextItemWidth(50 * UiHelpers.Scale);
|
||||
if (ImGui.InputInt("##Priority", ref priority, 0, 0))
|
||||
_currentPriority = priority;
|
||||
|
||||
if (ImGui.IsItemDeactivatedAfterEdit() && _currentPriority.HasValue)
|
||||
{
|
||||
if (_currentPriority != _settings.Priority)
|
||||
_collectionManager.Editor.SetModPriority(_collectionManager.Active.Current, _selector.Selected!, _currentPriority.Value);
|
||||
if (_currentPriority != _settings.Priority.Value)
|
||||
_collectionManager.Editor.SetModPriority(_collectionManager.Active.Current, _selector.Selected!, new ModPriority(_currentPriority.Value));
|
||||
|
||||
_currentPriority = null;
|
||||
}
|
||||
|
|
@ -179,7 +179,7 @@ public class ModPanelSettingsTab : ITab
|
|||
private void DrawSingleGroupCombo(IModGroup group, int groupIdx)
|
||||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var selectedOption = _empty ? (int)group.DefaultSettings : (int)_settings.Settings[groupIdx];
|
||||
var selectedOption = _empty ? group.DefaultSettings.AsIndex : _settings.Settings[groupIdx].AsIndex;
|
||||
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X * 3 / 4);
|
||||
using (var combo = ImRaii.Combo(string.Empty, group[selectedOption].Name))
|
||||
{
|
||||
|
|
@ -189,7 +189,8 @@ public class ModPanelSettingsTab : ITab
|
|||
id.Push(idx2);
|
||||
var option = group[idx2];
|
||||
if (ImGui.Selectable(option.Name, idx2 == selectedOption))
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, (uint)idx2);
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx,
|
||||
Setting.Single(idx2));
|
||||
|
||||
if (option.Description.Length > 0)
|
||||
ImGuiUtil.SelectableHelpMarker(option.Description);
|
||||
|
|
@ -210,8 +211,8 @@ public class ModPanelSettingsTab : ITab
|
|||
private void DrawSingleGroupRadio(IModGroup group, int groupIdx)
|
||||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var selectedOption = _empty ? (int)group.DefaultSettings : (int)_settings.Settings[groupIdx];
|
||||
var minWidth = Widget.BeginFramedGroup(group.Name, description:group.Description);
|
||||
var selectedOption = _empty ? group.DefaultSettings.AsIndex : _settings.Settings[groupIdx].AsIndex;
|
||||
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
|
||||
|
||||
DrawCollapseHandling(group, minWidth, DrawOptions);
|
||||
|
||||
|
|
@ -225,7 +226,8 @@ public class ModPanelSettingsTab : ITab
|
|||
using var i = ImRaii.PushId(idx);
|
||||
var option = group[idx];
|
||||
if (ImGui.RadioButton(option.Name, selectedOption == idx))
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, (uint)idx);
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx,
|
||||
Setting.Single(idx));
|
||||
|
||||
if (option.Description.Length <= 0)
|
||||
continue;
|
||||
|
|
@ -291,7 +293,17 @@ public class ModPanelSettingsTab : ITab
|
|||
{
|
||||
using var id = ImRaii.PushId(groupIdx);
|
||||
var flags = _empty ? group.DefaultSettings : _settings.Settings[groupIdx];
|
||||
var minWidth = Widget.BeginFramedGroup(group.Name, description: group.Description);
|
||||
var minWidth = Widget.BeginFramedGroup(group.Name, group.Description);
|
||||
|
||||
DrawCollapseHandling(group, minWidth, DrawOptions);
|
||||
|
||||
Widget.EndFramedGroup();
|
||||
var label = $"##multi{groupIdx}";
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
ImGui.OpenPopup($"##multi{groupIdx}");
|
||||
|
||||
DrawMultiPopup(group, groupIdx, label);
|
||||
return;
|
||||
|
||||
void DrawOptions()
|
||||
{
|
||||
|
|
@ -299,12 +311,11 @@ public class ModPanelSettingsTab : ITab
|
|||
{
|
||||
using var i = ImRaii.PushId(idx);
|
||||
var option = group[idx];
|
||||
var flag = 1u << idx;
|
||||
var setting = (flags & flag) != 0;
|
||||
var setting = flags.HasFlag(idx);
|
||||
|
||||
if (ImGui.Checkbox(option.Name, ref setting))
|
||||
{
|
||||
flags = setting ? flags | flag : flags & ~flag;
|
||||
flags = flags.SetBit(idx, setting);
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, flags);
|
||||
}
|
||||
|
||||
|
|
@ -315,14 +326,10 @@ public class ModPanelSettingsTab : ITab
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawCollapseHandling(group, minWidth, DrawOptions);
|
||||
|
||||
Widget.EndFramedGroup();
|
||||
var label = $"##multi{groupIdx}";
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
ImGui.OpenPopup($"##multi{groupIdx}");
|
||||
|
||||
private void DrawMultiPopup(IModGroup group, int groupIdx, string label)
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.PopupBorderSize, 1);
|
||||
using var popup = ImRaii.Popup(label);
|
||||
if (!popup)
|
||||
|
|
@ -331,12 +338,10 @@ public class ModPanelSettingsTab : ITab
|
|||
ImGui.TextUnformatted(group.Name);
|
||||
ImGui.Separator();
|
||||
if (ImGui.Selectable("Enable All"))
|
||||
{
|
||||
flags = group.Count == 32 ? uint.MaxValue : (1u << group.Count) - 1u;
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, flags);
|
||||
}
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx,
|
||||
Setting.AllBits(group.Count));
|
||||
|
||||
if (ImGui.Selectable("Disable All"))
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, 0);
|
||||
_collectionManager.Editor.SetModSetting(_collectionManager.Active.Current, _selector.Selected!, groupIdx, Setting.Zero);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ public class DebugTab : Window, ITab
|
|||
color.Pop();
|
||||
foreach (var (mod, paths, manips) in collection._cache!.ModData.Data.OrderBy(t => t.Item1.Name))
|
||||
{
|
||||
using var id = mod is TemporaryMod t ? PushId(t.Priority) : PushId(((Mod)mod).ModPath.Name);
|
||||
using var id = mod is TemporaryMod t ? PushId(t.Priority.Value) : PushId(((Mod)mod).ModPath.Name);
|
||||
using var node2 = TreeNode(mod.Name.Text);
|
||||
if (!node2)
|
||||
continue;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue