mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Extract ModCollectionIdentity.
This commit is contained in:
parent
fbbfe5e00d
commit
67305d507a
43 changed files with 270 additions and 252 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
||||||
Subproject commit d9caded5efb7c9db0a273a43bb5f6d53cf4ace7f
|
Subproject commit fcc96daa02633f673325c14aeea6b6b568924f1e
|
||||||
|
|
@ -8,7 +8,7 @@ namespace Penumbra.Api.Api;
|
||||||
public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : IPenumbraApiCollection, IApiService
|
public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : IPenumbraApiCollection, IApiService
|
||||||
{
|
{
|
||||||
public Dictionary<Guid, string> GetCollections()
|
public Dictionary<Guid, string> GetCollections()
|
||||||
=> collections.Storage.ToDictionary(c => c.Id, c => c.Name);
|
=> collections.Storage.ToDictionary(c => c.Identity.Id, c => c.Identity.Name);
|
||||||
|
|
||||||
public List<(Guid Id, string Name)> GetCollectionsByIdentifier(string identifier)
|
public List<(Guid Id, string Name)> GetCollectionsByIdentifier(string identifier)
|
||||||
{
|
{
|
||||||
|
|
@ -17,14 +17,14 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
|
||||||
|
|
||||||
var list = new List<(Guid Id, string Name)>(4);
|
var list = new List<(Guid Id, string Name)>(4);
|
||||||
if (Guid.TryParse(identifier, out var guid) && collections.Storage.ById(guid, out var collection) && collection != ModCollection.Empty)
|
if (Guid.TryParse(identifier, out var guid) && collections.Storage.ById(guid, out var collection) && collection != ModCollection.Empty)
|
||||||
list.Add((collection.Id, collection.Name));
|
list.Add((collection.Identity.Id, collection.Identity.Name));
|
||||||
else if (identifier.Length >= 8)
|
else if (identifier.Length >= 8)
|
||||||
list.AddRange(collections.Storage.Where(c => c.Identifier.StartsWith(identifier, StringComparison.OrdinalIgnoreCase))
|
list.AddRange(collections.Storage.Where(c => c.Identity.Identifier.StartsWith(identifier, StringComparison.OrdinalIgnoreCase))
|
||||||
.Select(c => (c.Id, c.Name)));
|
.Select(c => (c.Identity.Id, c.Identity.Name)));
|
||||||
|
|
||||||
list.AddRange(collections.Storage
|
list.AddRange(collections.Storage
|
||||||
.Where(c => string.Equals(c.Name, identifier, StringComparison.OrdinalIgnoreCase) && !list.Contains((c.Id, c.Name)))
|
.Where(c => string.Equals(c.Identity.Name, identifier, StringComparison.OrdinalIgnoreCase) && !list.Contains((c.Identity.Id, c.Identity.Name)))
|
||||||
.Select(c => (c.Id, c.Name)));
|
.Select(c => (c.Identity.Id, c.Identity.Name)));
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var collection = collections.Active.ByType((CollectionType)type);
|
var collection = collections.Active.ByType((CollectionType)type);
|
||||||
return collection == null ? null : (collection.Id, collection.Name);
|
return collection == null ? null : (collection.Identity.Id, collection.Identity.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal (Guid Id, string Name)? GetCollection(byte type)
|
internal (Guid Id, string Name)? GetCollection(byte type)
|
||||||
|
|
@ -64,17 +64,17 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
|
||||||
{
|
{
|
||||||
var id = helpers.AssociatedIdentifier(gameObjectIdx);
|
var id = helpers.AssociatedIdentifier(gameObjectIdx);
|
||||||
if (!id.IsValid)
|
if (!id.IsValid)
|
||||||
return (false, false, (collections.Active.Default.Id, collections.Active.Default.Name));
|
return (false, false, (collections.Active.Default.Identity.Id, collections.Active.Default.Identity.Name));
|
||||||
|
|
||||||
if (collections.Active.Individuals.TryGetValue(id, out var collection))
|
if (collections.Active.Individuals.TryGetValue(id, out var collection))
|
||||||
return (true, true, (collection.Id, collection.Name));
|
return (true, true, (collection.Identity.Id, collection.Identity.Name));
|
||||||
|
|
||||||
helpers.AssociatedCollection(gameObjectIdx, out collection);
|
helpers.AssociatedCollection(gameObjectIdx, out collection);
|
||||||
return (true, false, (collection.Id, collection.Name));
|
return (true, false, (collection.Identity.Id, collection.Identity.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid[] GetCollectionByName(string name)
|
public Guid[] GetCollectionByName(string name)
|
||||||
=> collections.Storage.Where(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase)).Select(c => c.Id).ToArray();
|
=> collections.Storage.Where(c => string.Equals(name, c.Identity.Name, StringComparison.OrdinalIgnoreCase)).Select(c => c.Identity.Id).ToArray();
|
||||||
|
|
||||||
public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollection(ApiCollectionType type, Guid? collectionId,
|
public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollection(ApiCollectionType type, Guid? collectionId,
|
||||||
bool allowCreateNew, bool allowDelete)
|
bool allowCreateNew, bool allowDelete)
|
||||||
|
|
@ -83,7 +83,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
|
||||||
return (PenumbraApiEc.InvalidArgument, null);
|
return (PenumbraApiEc.InvalidArgument, null);
|
||||||
|
|
||||||
var oldCollection = collections.Active.ByType((CollectionType)type);
|
var oldCollection = collections.Active.ByType((CollectionType)type);
|
||||||
var old = oldCollection != null ? (oldCollection.Id, oldCollection.Name) : new ValueTuple<Guid, string>?();
|
var old = oldCollection != null ? (oldCollection.Identity.Id, oldCollection.Identity.Name) : new ValueTuple<Guid, string>?();
|
||||||
if (collectionId == null)
|
if (collectionId == null)
|
||||||
{
|
{
|
||||||
if (old == null)
|
if (old == null)
|
||||||
|
|
@ -106,7 +106,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
|
||||||
|
|
||||||
collections.Active.CreateSpecialCollection((CollectionType)type);
|
collections.Active.CreateSpecialCollection((CollectionType)type);
|
||||||
}
|
}
|
||||||
else if (old.Value.Item1 == collection.Id)
|
else if (old.Value.Item1 == collection.Identity.Id)
|
||||||
{
|
{
|
||||||
return (PenumbraApiEc.NothingChanged, old);
|
return (PenumbraApiEc.NothingChanged, old);
|
||||||
}
|
}
|
||||||
|
|
@ -120,10 +120,10 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
|
||||||
{
|
{
|
||||||
var id = helpers.AssociatedIdentifier(gameObjectIdx);
|
var id = helpers.AssociatedIdentifier(gameObjectIdx);
|
||||||
if (!id.IsValid)
|
if (!id.IsValid)
|
||||||
return (PenumbraApiEc.InvalidIdentifier, (collections.Active.Default.Id, collections.Active.Default.Name));
|
return (PenumbraApiEc.InvalidIdentifier, (collections.Active.Default.Identity.Id, collections.Active.Default.Identity.Name));
|
||||||
|
|
||||||
var oldCollection = collections.Active.Individuals.TryGetValue(id, out var c) ? c : null;
|
var oldCollection = collections.Active.Individuals.TryGetValue(id, out var c) ? c : null;
|
||||||
var old = oldCollection != null ? (oldCollection.Id, oldCollection.Name) : new ValueTuple<Guid, string>?();
|
var old = oldCollection != null ? (oldCollection.Identity.Id, oldCollection.Identity.Name) : new ValueTuple<Guid, string>?();
|
||||||
if (collectionId == null)
|
if (collectionId == null)
|
||||||
{
|
{
|
||||||
if (old == null)
|
if (old == null)
|
||||||
|
|
@ -148,7 +148,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
|
||||||
var ids = collections.Active.Individuals.GetGroup(id);
|
var ids = collections.Active.Individuals.GetGroup(id);
|
||||||
collections.Active.CreateIndividualCollection(ids);
|
collections.Active.CreateIndividualCollection(ids);
|
||||||
}
|
}
|
||||||
else if (old.Value.Item1 == collection.Id)
|
else if (old.Value.Item1 == collection.Identity.Id)
|
||||||
{
|
{
|
||||||
return (PenumbraApiEc.NothingChanged, old);
|
return (PenumbraApiEc.NothingChanged, old);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ public class GameStateApi : IPenumbraApiGameState, IApiService, IDisposable
|
||||||
public unsafe (nint GameObject, (Guid Id, string Name) Collection) GetDrawObjectInfo(nint drawObject)
|
public unsafe (nint GameObject, (Guid Id, string Name) Collection) GetDrawObjectInfo(nint drawObject)
|
||||||
{
|
{
|
||||||
var data = _collectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
var data = _collectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
|
||||||
return (data.AssociatedGameObject, (data.ModCollection.Id, data.ModCollection.Name));
|
return (data.AssociatedGameObject, (Id: data.ModCollection.Identity.Id, Name: data.ModCollection.Identity.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetCutsceneParentIndex(int actorIdx)
|
public int GetCutsceneParentIndex(int actorIdx)
|
||||||
|
|
@ -93,5 +93,5 @@ public class GameStateApi : IPenumbraApiGameState, IApiService, IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCreatedCharacterBase(nint gameObject, ModCollection collection, nint drawObject)
|
private void OnCreatedCharacterBase(nint gameObject, ModCollection collection, nint drawObject)
|
||||||
=> CreatedCharacterBase?.Invoke(gameObject, collection.Id, drawObject);
|
=> CreatedCharacterBase?.Invoke(gameObject, collection.Identity.Id, drawObject);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
|
||||||
if (!_collectionManager.Storage.ById(collectionId, out var collection))
|
if (!_collectionManager.Storage.ById(collectionId, out var collection))
|
||||||
return (PenumbraApiEc.CollectionMissing, null);
|
return (PenumbraApiEc.CollectionMissing, null);
|
||||||
|
|
||||||
var settings = collection.Id == Guid.Empty
|
var settings = collection.Identity.Id == Guid.Empty
|
||||||
? null
|
? null
|
||||||
: ignoreInheritance
|
: ignoreInheritance
|
||||||
? collection.Settings[mod.Index]
|
? collection.Settings[mod.Index]
|
||||||
|
|
@ -217,7 +217,7 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
|
||||||
var collection = _collectionResolver.PlayerCollection();
|
var collection = _collectionResolver.PlayerCollection();
|
||||||
var (settings, parent) = collection[mod.Index];
|
var (settings, parent) = collection[mod.Index];
|
||||||
if (settings is { Enabled: true })
|
if (settings is { Enabled: true })
|
||||||
ModSettingChanged?.Invoke(ModSettingChange.Edited, collection.Id, mod.Identifier, parent != collection);
|
ModSettingChanged?.Invoke(ModSettingChange.Edited, collection.Identity.Id, mod.Identifier, parent != collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
|
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
|
||||||
|
|
@ -227,7 +227,7 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting _1, int _2, bool inherited)
|
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting _1, int _2, bool inherited)
|
||||||
=> ModSettingChanged?.Invoke(type, collection.Id, mod?.ModPath.Name ?? string.Empty, inherited);
|
=> ModSettingChanged?.Invoke(type, collection.Identity.Id, mod?.ModPath.Name ?? string.Empty, inherited);
|
||||||
|
|
||||||
private void OnModOptionEdited(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container,
|
private void OnModOptionEdited(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container,
|
||||||
int moveIndex)
|
int moveIndex)
|
||||||
|
|
|
||||||
|
|
@ -146,10 +146,10 @@ public class TemporaryIpcTester(
|
||||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||||
{
|
{
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGuiUtil.CopyOnClickSelectable(collection.Identifier);
|
ImGuiUtil.CopyOnClickSelectable(collection.Identity.Identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiUtil.DrawTableColumn(collection.Name);
|
ImGuiUtil.DrawTableColumn(collection.Identity.Name);
|
||||||
ImGuiUtil.DrawTableColumn(collection.ResolvedFiles.Count.ToString());
|
ImGuiUtil.DrawTableColumn(collection.ResolvedFiles.Count.ToString());
|
||||||
ImGuiUtil.DrawTableColumn(collection.MetaCache?.Count.ToString() ?? "0");
|
ImGuiUtil.DrawTableColumn(collection.MetaCache?.Count.ToString() ?? "0");
|
||||||
ImGuiUtil.DrawTableColumn(string.Join(", ",
|
ImGuiUtil.DrawTableColumn(string.Join(", ",
|
||||||
|
|
@ -199,7 +199,7 @@ public class TemporaryIpcTester(
|
||||||
{
|
{
|
||||||
PrintList("All", tempMods.ModsForAllCollections);
|
PrintList("All", tempMods.ModsForAllCollections);
|
||||||
foreach (var (collection, list) in tempMods.Mods)
|
foreach (var (collection, list) in tempMods.Mods)
|
||||||
PrintList(collection.Name, list);
|
PrintList(collection.Identity.Name, list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,13 +85,13 @@ public class TempModManager : IDisposable, IService
|
||||||
{
|
{
|
||||||
if (removed)
|
if (removed)
|
||||||
{
|
{
|
||||||
Penumbra.Log.Verbose($"Removing temporary Mod {mod.Name} from {collection.AnonymizedName}.");
|
Penumbra.Log.Verbose($"Removing temporary Mod {mod.Name} from {collection.Identity.AnonymizedName}.");
|
||||||
collection.Remove(mod);
|
collection.Remove(mod);
|
||||||
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.False, 0, false);
|
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.False, 0, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Penumbra.Log.Verbose($"Adding {(created ? "new " : string.Empty)}temporary Mod {mod.Name} to {collection.AnonymizedName}.");
|
Penumbra.Log.Verbose($"Adding {(created ? "new " : string.Empty)}temporary Mod {mod.Name} to {collection.Identity.AnonymizedName}.");
|
||||||
collection.Apply(mod, created);
|
collection.Apply(mod, created);
|
||||||
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.True, 0, false);
|
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.True, 0, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ public sealed class CollectionCache : IDisposable
|
||||||
public int Calculating = -1;
|
public int Calculating = -1;
|
||||||
|
|
||||||
public string AnonymizedName
|
public string AnonymizedName
|
||||||
=> _collection.AnonymizedName;
|
=> _collection.Identity.AnonymizedName;
|
||||||
|
|
||||||
public IEnumerable<SingleArray<ModConflicts>> AllConflicts
|
public IEnumerable<SingleArray<ModConflicts>> AllConflicts
|
||||||
=> ConflictDict.Values;
|
=> ConflictDict.Values;
|
||||||
|
|
|
||||||
|
|
@ -114,16 +114,16 @@ public class CollectionCacheManager : IDisposable, IService
|
||||||
/// <summary> Only creates a new cache, does not update an existing one. </summary>
|
/// <summary> Only creates a new cache, does not update an existing one. </summary>
|
||||||
public bool CreateCache(ModCollection collection)
|
public bool CreateCache(ModCollection collection)
|
||||||
{
|
{
|
||||||
if (collection.Index == ModCollection.Empty.Index)
|
if (collection.Identity.Index == ModCollection.Empty.Identity.Index)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (collection._cache != null)
|
if (collection._cache != null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
collection._cache = new CollectionCache(this, collection);
|
collection._cache = new CollectionCache(this, collection);
|
||||||
if (collection.Index > 0)
|
if (collection.Identity.Index > 0)
|
||||||
Interlocked.Increment(ref _count);
|
Interlocked.Increment(ref _count);
|
||||||
Penumbra.Log.Verbose($"Created new cache for collection {collection.AnonymizedName}.");
|
Penumbra.Log.Verbose($"Created new cache for collection {collection.Identity.AnonymizedName}.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,32 +132,32 @@ public class CollectionCacheManager : IDisposable, IService
|
||||||
/// Does not create caches.
|
/// Does not create caches.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void CalculateEffectiveFileList(ModCollection collection)
|
public void CalculateEffectiveFileList(ModCollection collection)
|
||||||
=> _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Identifier,
|
=> _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Identity.Identifier,
|
||||||
() => CalculateEffectiveFileListInternal(collection));
|
() => CalculateEffectiveFileListInternal(collection));
|
||||||
|
|
||||||
private void CalculateEffectiveFileListInternal(ModCollection collection)
|
private void CalculateEffectiveFileListInternal(ModCollection collection)
|
||||||
{
|
{
|
||||||
// Skip the empty collection.
|
// Skip the empty collection.
|
||||||
if (collection.Index == 0)
|
if (collection.Identity.Index == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Penumbra.Log.Debug($"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.AnonymizedName}");
|
Penumbra.Log.Debug($"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.Identity.AnonymizedName}");
|
||||||
if (!collection.HasCache)
|
if (!collection.HasCache)
|
||||||
{
|
{
|
||||||
Penumbra.Log.Error(
|
Penumbra.Log.Error(
|
||||||
$"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.AnonymizedName} failed, no cache exists.");
|
$"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.Identity.AnonymizedName} failed, no cache exists.");
|
||||||
}
|
}
|
||||||
else if (collection._cache!.Calculating != -1)
|
else if (collection._cache!.Calculating != -1)
|
||||||
{
|
{
|
||||||
Penumbra.Log.Error(
|
Penumbra.Log.Error(
|
||||||
$"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.AnonymizedName} failed, already in calculation on [{collection._cache!.Calculating}].");
|
$"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.Identity.AnonymizedName} failed, already in calculation on [{collection._cache!.Calculating}].");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FullRecalculation(collection);
|
FullRecalculation(collection);
|
||||||
|
|
||||||
Penumbra.Log.Debug(
|
Penumbra.Log.Debug(
|
||||||
$"[{Environment.CurrentManagedThreadId}] Recalculation of effective file list for {collection.AnonymizedName} finished.");
|
$"[{Environment.CurrentManagedThreadId}] Recalculation of effective file list for {collection.Identity.AnonymizedName} finished.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +213,7 @@ public class CollectionCacheManager : IDisposable, IService
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RemoveCache(old);
|
RemoveCache(old);
|
||||||
if (type is not CollectionType.Inactive && newCollection != null && newCollection.Index != 0 && CreateCache(newCollection))
|
if (type is not CollectionType.Inactive && newCollection != null && newCollection.Identity.Index != 0 && CreateCache(newCollection))
|
||||||
CalculateEffectiveFileList(newCollection);
|
CalculateEffectiveFileList(newCollection);
|
||||||
|
|
||||||
if (type is CollectionType.Default)
|
if (type is CollectionType.Default)
|
||||||
|
|
@ -258,12 +258,12 @@ public class CollectionCacheManager : IDisposable, IService
|
||||||
private void RemoveCache(ModCollection? collection)
|
private void RemoveCache(ModCollection? collection)
|
||||||
{
|
{
|
||||||
if (collection != null
|
if (collection != null
|
||||||
&& collection.Index > ModCollection.Empty.Index
|
&& collection.Identity.Index > ModCollection.Empty.Identity.Index
|
||||||
&& collection.Index != _active.Default.Index
|
&& collection.Identity.Index != _active.Default.Identity.Index
|
||||||
&& collection.Index != _active.Interface.Index
|
&& collection.Identity.Index != _active.Interface.Identity.Index
|
||||||
&& collection.Index != _active.Current.Index
|
&& collection.Identity.Index != _active.Current.Identity.Index
|
||||||
&& _active.SpecialAssignments.All(c => c.Value.Index != collection.Index)
|
&& _active.SpecialAssignments.All(c => c.Value.Identity.Index != collection.Identity.Index)
|
||||||
&& _active.Individuals.All(c => c.Collection.Index != collection.Index))
|
&& _active.Individuals.All(c => c.Collection.Identity.Index != collection.Identity.Index))
|
||||||
ClearCache(collection);
|
ClearCache(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,9 +359,9 @@ public class CollectionCacheManager : IDisposable, IService
|
||||||
|
|
||||||
collection._cache!.Dispose();
|
collection._cache!.Dispose();
|
||||||
collection._cache = null;
|
collection._cache = null;
|
||||||
if (collection.Index > 0)
|
if (collection.Identity.Index > 0)
|
||||||
Interlocked.Decrement(ref _count);
|
Interlocked.Decrement(ref _count);
|
||||||
Penumbra.Log.Verbose($"Cleared cache of collection {collection.AnonymizedName}.");
|
Penumbra.Log.Verbose($"Cleared cache of collection {collection.Identity.AnonymizedName}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ public sealed class CollectionAutoSelector : IService, IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var collection = _resolver.PlayerCollection();
|
var collection = _resolver.PlayerCollection();
|
||||||
Penumbra.Log.Debug($"Setting current collection to {collection.Identifier} through automatic collection selection.");
|
Penumbra.Log.Debug($"Setting current collection to {collection.Identity.Identifier} through automatic collection selection.");
|
||||||
_collections.SetCollection(collection, CollectionType.Current);
|
_collections.SetCollection(collection, CollectionType.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public static class ActiveCollectionMigration
|
||||||
if (!storage.ByName(collectionName, out var collection))
|
if (!storage.ByName(collectionName, out var collection))
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Last choice of <{player}>'s Collection {collectionName} is not available, reset to {ModCollection.Empty.Name}.", NotificationType.Warning);
|
$"Last choice of <{player}>'s Collection {collectionName} is not available, reset to {ModCollection.Empty.Identity.Name}.", NotificationType.Warning);
|
||||||
dict.Add(player, ModCollection.Empty);
|
dict.Add(player, ModCollection.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (oldCollection == null || collection == oldCollection || collection.Index >= _storage.Count)
|
if (oldCollection == null || collection == oldCollection || collection.Identity.Index >= _storage.Count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (collectionType)
|
switch (collectionType)
|
||||||
|
|
@ -262,13 +262,13 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
var jObj = new JObject
|
var jObj = new JObject
|
||||||
{
|
{
|
||||||
{ nameof(Version), Version },
|
{ nameof(Version), Version },
|
||||||
{ nameof(Default), Default.Id },
|
{ nameof(Default), Default.Identity.Id },
|
||||||
{ nameof(Interface), Interface.Id },
|
{ nameof(Interface), Interface.Identity.Id },
|
||||||
{ nameof(Current), Current.Id },
|
{ nameof(Current), Current.Identity.Id },
|
||||||
};
|
};
|
||||||
foreach (var (type, collection) in SpecialCollections.WithIndex().Where(p => p.Value != null)
|
foreach (var (type, collection) in SpecialCollections.WithIndex().Where(p => p.Value != null)
|
||||||
.Select(p => ((CollectionType)p.Index, p.Value!)))
|
.Select(p => ((CollectionType)p.Index, p.Value!)))
|
||||||
jObj.Add(type.ToString(), collection.Id);
|
jObj.Add(type.ToString(), collection.Identity.Id);
|
||||||
|
|
||||||
jObj.Add(nameof(Individuals), Individuals.ToJObject());
|
jObj.Add(nameof(Individuals), Individuals.ToJObject());
|
||||||
using var j = new JsonTextWriter(writer);
|
using var j = new JsonTextWriter(writer);
|
||||||
|
|
@ -300,7 +300,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
if (oldCollection == Interface)
|
if (oldCollection == Interface)
|
||||||
SetCollection(ModCollection.Empty, CollectionType.Interface);
|
SetCollection(ModCollection.Empty, CollectionType.Interface);
|
||||||
if (oldCollection == Current)
|
if (oldCollection == Current)
|
||||||
SetCollection(Default.Index > ModCollection.Empty.Index ? Default : _storage.DefaultNamed, CollectionType.Current);
|
SetCollection(Default.Identity.Index > ModCollection.Empty.Identity.Index ? Default : _storage.DefaultNamed, CollectionType.Current);
|
||||||
|
|
||||||
for (var i = 0; i < SpecialCollections.Length; ++i)
|
for (var i = 0; i < SpecialCollections.Length; ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -325,11 +325,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
{
|
{
|
||||||
var configChanged = false;
|
var configChanged = false;
|
||||||
// Load the default collection. If the name does not exist take the empty collection.
|
// Load the default collection. If the name does not exist take the empty collection.
|
||||||
var defaultName = jObject[nameof(Default)]?.ToObject<string>() ?? ModCollection.Empty.Name;
|
var defaultName = jObject[nameof(Default)]?.ToObject<string>() ?? ModCollection.Empty.Identity.Name;
|
||||||
if (!_storage.ByName(defaultName, out var defaultCollection))
|
if (!_storage.ByName(defaultName, out var defaultCollection))
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Last choice of {TutorialService.DefaultCollection} {defaultName} is not available, reset to {ModCollection.Empty.Name}.",
|
$"Last choice of {TutorialService.DefaultCollection} {defaultName} is not available, reset to {ModCollection.Empty.Identity.Name}.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
Default = ModCollection.Empty;
|
Default = ModCollection.Empty;
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
|
|
@ -340,11 +340,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the interface collection. If no string is set, use the name of whatever was set as Default.
|
// Load the interface collection. If no string is set, use the name of whatever was set as Default.
|
||||||
var interfaceName = jObject[nameof(Interface)]?.ToObject<string>() ?? Default.Name;
|
var interfaceName = jObject[nameof(Interface)]?.ToObject<string>() ?? Default.Identity.Name;
|
||||||
if (!_storage.ByName(interfaceName, out var interfaceCollection))
|
if (!_storage.ByName(interfaceName, out var interfaceCollection))
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Last choice of {TutorialService.InterfaceCollection} {interfaceName} is not available, reset to {ModCollection.Empty.Name}.",
|
$"Last choice of {TutorialService.InterfaceCollection} {interfaceName} is not available, reset to {ModCollection.Empty.Identity.Name}.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
Interface = ModCollection.Empty;
|
Interface = ModCollection.Empty;
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
|
|
@ -355,11 +355,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the current collection.
|
// Load the current collection.
|
||||||
var currentName = jObject[nameof(Current)]?.ToObject<string>() ?? Default.Name;
|
var currentName = jObject[nameof(Current)]?.ToObject<string>() ?? Default.Identity.Name;
|
||||||
if (!_storage.ByName(currentName, out var currentCollection))
|
if (!_storage.ByName(currentName, out var currentCollection))
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Last choice of {TutorialService.SelectedCollection} {currentName} is not available, reset to {ModCollection.DefaultCollectionName}.",
|
$"Last choice of {TutorialService.SelectedCollection} {currentName} is not available, reset to {ModCollectionIdentity.DefaultCollectionName}.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
Current = _storage.DefaultNamed;
|
Current = _storage.DefaultNamed;
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
|
|
@ -404,7 +404,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
if (!_storage.ById(defaultId, out var defaultCollection))
|
if (!_storage.ById(defaultId, out var defaultCollection))
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Last choice of {TutorialService.DefaultCollection} {defaultId} is not available, reset to {ModCollection.Empty.Name}.",
|
$"Last choice of {TutorialService.DefaultCollection} {defaultId} is not available, reset to {ModCollection.Empty.Identity.Name}.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
Default = ModCollection.Empty;
|
Default = ModCollection.Empty;
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
|
|
@ -415,11 +415,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the interface collection. If no string is set, use the name of whatever was set as Default.
|
// Load the interface collection. If no string is set, use the name of whatever was set as Default.
|
||||||
var interfaceId = jObject[nameof(Interface)]?.ToObject<Guid>() ?? Default.Id;
|
var interfaceId = jObject[nameof(Interface)]?.ToObject<Guid>() ?? Default.Identity.Id;
|
||||||
if (!_storage.ById(interfaceId, out var interfaceCollection))
|
if (!_storage.ById(interfaceId, out var interfaceCollection))
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Last choice of {TutorialService.InterfaceCollection} {interfaceId} is not available, reset to {ModCollection.Empty.Name}.",
|
$"Last choice of {TutorialService.InterfaceCollection} {interfaceId} is not available, reset to {ModCollection.Empty.Identity.Name}.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
Interface = ModCollection.Empty;
|
Interface = ModCollection.Empty;
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
|
|
@ -430,11 +430,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the current collection.
|
// Load the current collection.
|
||||||
var currentId = jObject[nameof(Current)]?.ToObject<Guid>() ?? _storage.DefaultNamed.Id;
|
var currentId = jObject[nameof(Current)]?.ToObject<Guid>() ?? _storage.DefaultNamed.Identity.Id;
|
||||||
if (!_storage.ById(currentId, out var currentCollection))
|
if (!_storage.ById(currentId, out var currentCollection))
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Last choice of {TutorialService.SelectedCollection} {currentId} is not available, reset to {ModCollection.DefaultCollectionName}.",
|
$"Last choice of {TutorialService.SelectedCollection} {currentId} is not available, reset to {ModCollectionIdentity.DefaultCollectionName}.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
Current = _storage.DefaultNamed;
|
Current = _storage.DefaultNamed;
|
||||||
configChanged = true;
|
configChanged = true;
|
||||||
|
|
@ -587,7 +587,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
case IdentifierType.Player when id.HomeWorld != ushort.MaxValue:
|
case IdentifierType.Player when id.HomeWorld != ushort.MaxValue:
|
||||||
{
|
{
|
||||||
var global = ByType(CollectionType.Individual, _actors.CreatePlayer(id.PlayerName, ushort.MaxValue));
|
var global = ByType(CollectionType.Individual, _actors.CreatePlayer(id.PlayerName, ushort.MaxValue));
|
||||||
return global?.Index == checkAssignment.Index
|
return (global != null ? global.Identity.Index : null) == checkAssignment.Identity.Index
|
||||||
? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."
|
? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
}
|
}
|
||||||
|
|
@ -596,12 +596,12 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
{
|
{
|
||||||
var global = ByType(CollectionType.Individual,
|
var global = ByType(CollectionType.Individual,
|
||||||
_actors.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId));
|
_actors.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId));
|
||||||
if (global?.Index == checkAssignment.Index)
|
if ((global != null ? global.Identity.Index : null) == checkAssignment.Identity.Index)
|
||||||
return "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it.";
|
return "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it.";
|
||||||
}
|
}
|
||||||
|
|
||||||
var unowned = ByType(CollectionType.Individual, _actors.CreateNpc(id.Kind, id.DataId));
|
var unowned = ByType(CollectionType.Individual, _actors.CreateNpc(id.Kind, id.DataId));
|
||||||
return unowned?.Index == checkAssignment.Index
|
return (unowned != null ? unowned.Identity.Index : null) == checkAssignment.Identity.Index
|
||||||
? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it."
|
? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it."
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
}
|
}
|
||||||
|
|
@ -617,7 +617,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
if (maleNpc == null)
|
if (maleNpc == null)
|
||||||
{
|
{
|
||||||
maleNpc = Default;
|
maleNpc = Default;
|
||||||
if (maleNpc.Index != checkAssignment.Index)
|
if (maleNpc.Identity.Index != checkAssignment.Identity.Index)
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
collection1 = CollectionType.Default;
|
collection1 = CollectionType.Default;
|
||||||
|
|
@ -626,7 +626,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
if (femaleNpc == null)
|
if (femaleNpc == null)
|
||||||
{
|
{
|
||||||
femaleNpc = Default;
|
femaleNpc = Default;
|
||||||
if (femaleNpc.Index != checkAssignment.Index)
|
if (femaleNpc.Identity.Index != checkAssignment.Identity.Index)
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
collection2 = CollectionType.Default;
|
collection2 = CollectionType.Default;
|
||||||
|
|
@ -646,7 +646,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
|
||||||
if (assignment == null)
|
if (assignment == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (assignment.Index == checkAssignment.Index)
|
if (assignment.Identity.Index == checkAssignment.Identity.Index)
|
||||||
return
|
return
|
||||||
$"Assignment is currently redundant due to overwriting {parentType.ToName()} with an identical collection.\nYou can remove it.";
|
$"Assignment is currently redundant due to overwriting {parentType.ToName()} with an identical collection.\nYou can remove it.";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
public ModCollection CreateFromData(Guid id, string name, int version, Dictionary<string, ModSettings.SavedSettings> allSettings,
|
public ModCollection CreateFromData(Guid id, string name, int version, Dictionary<string, ModSettings.SavedSettings> allSettings,
|
||||||
IReadOnlyList<string> inheritances)
|
IReadOnlyList<string> inheritances)
|
||||||
{
|
{
|
||||||
var newCollection = ModCollection.CreateFromData(_saveService, _modStorage, id, name, CurrentCollectionId, version, Count, allSettings,
|
var newCollection = ModCollection.CreateFromData(_saveService, _modStorage, new ModCollectionIdentity(id, CurrentCollectionId, name, Count), version, allSettings,
|
||||||
inheritances);
|
inheritances);
|
||||||
_collectionsByLocal[CurrentCollectionId] = newCollection;
|
_collectionsByLocal[CurrentCollectionId] = newCollection;
|
||||||
CurrentCollectionId += 1;
|
CurrentCollectionId += 1;
|
||||||
|
|
@ -57,7 +57,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(ModCollection collection)
|
public void Delete(ModCollection collection)
|
||||||
=> _collectionsByLocal.Remove(collection.LocalId);
|
=> _collectionsByLocal.Remove(collection.Identity.LocalId);
|
||||||
|
|
||||||
/// <remarks> The empty collection is always available at Index 0. </remarks>
|
/// <remarks> The empty collection is always available at Index 0. </remarks>
|
||||||
private readonly List<ModCollection> _collections =
|
private readonly List<ModCollection> _collections =
|
||||||
|
|
@ -92,7 +92,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
public bool ByName(string name, [NotNullWhen(true)] out ModCollection? collection)
|
public bool ByName(string name, [NotNullWhen(true)] out ModCollection? collection)
|
||||||
{
|
{
|
||||||
if (name.Length != 0)
|
if (name.Length != 0)
|
||||||
return _collections.FindFirst(c => string.Equals(c.Name, name, StringComparison.OrdinalIgnoreCase), out collection);
|
return _collections.FindFirst(c => string.Equals(c.Identity.Name, name, StringComparison.OrdinalIgnoreCase), out collection);
|
||||||
|
|
||||||
collection = ModCollection.Empty;
|
collection = ModCollection.Empty;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -102,7 +102,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
public bool ById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
|
public bool ById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
|
||||||
{
|
{
|
||||||
if (id != Guid.Empty)
|
if (id != Guid.Empty)
|
||||||
return _collections.FindFirst(c => c.Id == id, out collection);
|
return _collections.FindFirst(c => c.Identity.Id == id, out collection);
|
||||||
|
|
||||||
collection = ModCollection.Empty;
|
collection = ModCollection.Empty;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -158,7 +158,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
var newCollection = Create(name, _collections.Count, duplicate);
|
var newCollection = Create(name, _collections.Count, duplicate);
|
||||||
_collections.Add(newCollection);
|
_collections.Add(newCollection);
|
||||||
_saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection));
|
_saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection));
|
||||||
Penumbra.Messager.NotificationMessage($"Created new collection {newCollection.AnonymizedName}.", NotificationType.Success, false);
|
Penumbra.Messager.NotificationMessage($"Created new collection {newCollection.Identity.AnonymizedName}.", NotificationType.Success, false);
|
||||||
_communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty);
|
_communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -168,13 +168,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RemoveCollection(ModCollection collection)
|
public bool RemoveCollection(ModCollection collection)
|
||||||
{
|
{
|
||||||
if (collection.Index <= ModCollection.Empty.Index || collection.Index >= _collections.Count)
|
if (collection.Identity.Index <= ModCollection.Empty.Identity.Index || collection.Identity.Index >= _collections.Count)
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage("Can not remove the empty collection.", NotificationType.Error, false);
|
Penumbra.Messager.NotificationMessage("Can not remove the empty collection.", NotificationType.Error, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collection.Index == DefaultNamed.Index)
|
if (collection.Identity.Index == DefaultNamed.Identity.Index)
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage("Can not remove the default collection.", NotificationType.Error, false);
|
Penumbra.Messager.NotificationMessage("Can not remove the default collection.", NotificationType.Error, false);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -182,13 +182,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
|
|
||||||
Delete(collection);
|
Delete(collection);
|
||||||
_saveService.ImmediateDelete(new ModCollectionSave(_modStorage, collection));
|
_saveService.ImmediateDelete(new ModCollectionSave(_modStorage, collection));
|
||||||
_collections.RemoveAt(collection.Index);
|
_collections.RemoveAt(collection.Identity.Index);
|
||||||
// Update indices.
|
// Update indices.
|
||||||
for (var i = collection.Index; i < Count; ++i)
|
for (var i = collection.Identity.Index; i < Count; ++i)
|
||||||
_collections[i].Index = i;
|
_collections[i].Identity.Index = i;
|
||||||
_collectionsByLocal.Remove(collection.LocalId);
|
_collectionsByLocal.Remove(collection.Identity.LocalId);
|
||||||
|
|
||||||
Penumbra.Messager.NotificationMessage($"Deleted collection {collection.AnonymizedName}.", NotificationType.Success, false);
|
Penumbra.Messager.NotificationMessage($"Deleted collection {collection.Identity.AnonymizedName}.", NotificationType.Success, false);
|
||||||
_communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty);
|
_communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -246,13 +246,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
{
|
{
|
||||||
File.Move(file.FullName, correctName, false);
|
File.Move(file.FullName, correctName, false);
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Collection {file.Name} does not correspond to {collection.Identifier}, renamed.",
|
$"Collection {file.Name} does not correspond to {collection.Identity.Identifier}, renamed.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Collection {file.Name} does not correspond to {collection.Identifier}, rename failed:\n{ex}",
|
$"Collection {file.Name} does not correspond to {collection.Identity.Identifier}, rename failed:\n{ex}",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -273,7 +273,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(e,
|
Penumbra.Messager.NotificationMessage(e,
|
||||||
$"Collection {file.Name} does not correspond to {collection.Identifier}, but could not rename.",
|
$"Collection {file.Name} does not correspond to {collection.Identity.Identifier}, but could not rename.",
|
||||||
NotificationType.Error);
|
NotificationType.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,14 +291,14 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ModCollection SetDefaultNamedCollection()
|
private ModCollection SetDefaultNamedCollection()
|
||||||
{
|
{
|
||||||
if (ByName(ModCollection.DefaultCollectionName, out var collection))
|
if (ByName(ModCollectionIdentity.DefaultCollectionName, out var collection))
|
||||||
return collection;
|
return collection;
|
||||||
|
|
||||||
if (AddCollection(ModCollection.DefaultCollectionName, null))
|
if (AddCollection(ModCollectionIdentity.DefaultCollectionName, null))
|
||||||
return _collections[^1];
|
return _collections[^1];
|
||||||
|
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Unknown problem creating a collection with the name {ModCollection.DefaultCollectionName}, which is required to exist.",
|
$"Unknown problem creating a collection with the name {ModCollectionIdentity.DefaultCollectionName}, which is required to exist.",
|
||||||
NotificationType.Error);
|
NotificationType.Error);
|
||||||
return Count > 1 ? _collections[1] : _collections[0];
|
return Count > 1 ? _collections[1] : _collections[0];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ public partial class IndividualCollections
|
||||||
foreach (var (name, identifiers, collection) in Assignments)
|
foreach (var (name, identifiers, collection) in Assignments)
|
||||||
{
|
{
|
||||||
var tmp = identifiers[0].ToJson();
|
var tmp = identifiers[0].ToJson();
|
||||||
tmp.Add("Collection", collection.Id);
|
tmp.Add("Collection", collection.Identity.Id);
|
||||||
tmp.Add("Display", name);
|
tmp.Add("Display", name);
|
||||||
ret.Add(tmp);
|
ret.Add(tmp);
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,7 @@ public partial class IndividualCollections
|
||||||
Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}].");
|
Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}].");
|
||||||
else
|
else
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Could not migrate {name} ({collection.AnonymizedName}) which was assumed to be a {kind.ToName()} with IDs [{ids}], please look through your individual collections.",
|
$"Could not migrate {name} ({collection.Identity.AnonymizedName}) which was assumed to be a {kind.ToName()} with IDs [{ids}], please look through your individual collections.",
|
||||||
NotificationType.Error);
|
NotificationType.Error);
|
||||||
}
|
}
|
||||||
// If it is not a valid NPC name, check if it can be a player name.
|
// If it is not a valid NPC name, check if it can be a player name.
|
||||||
|
|
@ -192,16 +192,16 @@ public partial class IndividualCollections
|
||||||
var shortName = string.Join(" ", name.Split().Select(n => $"{n[0]}."));
|
var shortName = string.Join(" ", name.Split().Select(n => $"{n[0]}."));
|
||||||
// Try to migrate the player name without logging full names.
|
// Try to migrate the player name without logging full names.
|
||||||
if (Add($"{name} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", [identifier], collection))
|
if (Add($"{name} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", [identifier], collection))
|
||||||
Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier.");
|
Penumbra.Log.Information($"Migrated {shortName} ({collection.Identity.AnonymizedName}) to Player Identifier.");
|
||||||
else
|
else
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Could not migrate {shortName} ({collection.AnonymizedName}), please look through your individual collections.",
|
$"Could not migrate {shortName} ({collection.Identity.AnonymizedName}), please look through your individual collections.",
|
||||||
NotificationType.Error);
|
NotificationType.Error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Could not migrate {name} ({collection.AnonymizedName}), which can not be a player name nor is it a known NPC name, please look through your individual collections.",
|
$"Could not migrate {name} ({collection.Identity.AnonymizedName}), which can not be a player name nor is it a known NPC name, please look through your individual collections.",
|
||||||
NotificationType.Error);
|
NotificationType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ public class InheritanceManager : IDisposable, IService
|
||||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
|
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
|
||||||
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
|
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
|
||||||
RecurseInheritanceChanges(inheritor);
|
RecurseInheritanceChanges(inheritor);
|
||||||
Penumbra.Log.Debug($"Removed {parent.AnonymizedName} from {inheritor.AnonymizedName} inheritances.");
|
Penumbra.Log.Debug($"Removed {parent.Identity.AnonymizedName} from {inheritor.Identity.AnonymizedName} inheritances.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Order in the inheritance list is relevant. </summary>
|
/// <summary> Order in the inheritance list is relevant. </summary>
|
||||||
|
|
@ -101,7 +101,7 @@ public class InheritanceManager : IDisposable, IService
|
||||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
|
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
|
||||||
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
|
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
|
||||||
RecurseInheritanceChanges(inheritor);
|
RecurseInheritanceChanges(inheritor);
|
||||||
Penumbra.Log.Debug($"Moved {inheritor.AnonymizedName}s inheritance {from} to {to}.");
|
Penumbra.Log.Debug($"Moved {inheritor.Identity.AnonymizedName}s inheritance {from} to {to}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="AddInheritance(ModCollection, ModCollection)"/>
|
/// <inheritdoc cref="AddInheritance(ModCollection, ModCollection)"/>
|
||||||
|
|
@ -119,7 +119,7 @@ public class InheritanceManager : IDisposable, IService
|
||||||
RecurseInheritanceChanges(inheritor);
|
RecurseInheritanceChanges(inheritor);
|
||||||
}
|
}
|
||||||
|
|
||||||
Penumbra.Log.Debug($"Added {parent.AnonymizedName} to {inheritor.AnonymizedName} inheritances.");
|
Penumbra.Log.Debug($"Added {parent.Identity.AnonymizedName} to {inheritor.Identity.AnonymizedName} inheritances.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,23 +143,23 @@ public class InheritanceManager : IDisposable, IService
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
changes = true;
|
changes = true;
|
||||||
Penumbra.Messager.NotificationMessage($"{collection.Name} can not inherit from {subCollection.Name}, removed.",
|
Penumbra.Messager.NotificationMessage($"{collection.Identity.Name} can not inherit from {subCollection.Identity.Name}, removed.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
}
|
}
|
||||||
else if (_storage.ByName(subCollectionName, out subCollection))
|
else if (_storage.ByName(subCollectionName, out subCollection))
|
||||||
{
|
{
|
||||||
changes = true;
|
changes = true;
|
||||||
Penumbra.Log.Information($"Migrating inheritance for {collection.AnonymizedName} from name to GUID.");
|
Penumbra.Log.Information($"Migrating inheritance for {collection.Identity.AnonymizedName} from name to GUID.");
|
||||||
if (AddInheritance(collection, subCollection, false))
|
if (AddInheritance(collection, subCollection, false))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Penumbra.Messager.NotificationMessage($"{collection.Name} can not inherit from {subCollection.Name}, removed.",
|
Penumbra.Messager.NotificationMessage($"{collection.Identity.Name} can not inherit from {subCollection.Identity.Name}, removed.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Penumbra.Messager.NotificationMessage(
|
Penumbra.Messager.NotificationMessage(
|
||||||
$"Inherited collection {subCollectionName} for {collection.AnonymizedName} does not exist, it was removed.",
|
$"Inherited collection {subCollectionName} for {collection.Identity.AnonymizedName} does not exist, it was removed.",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
changes = true;
|
changes = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ public class TempCollectionManager : IDisposable, IService
|
||||||
=> _customCollections.Values;
|
=> _customCollections.Values;
|
||||||
|
|
||||||
public bool CollectionByName(string name, [NotNullWhen(true)] out ModCollection? collection)
|
public bool CollectionByName(string name, [NotNullWhen(true)] out ModCollection? collection)
|
||||||
=> _customCollections.Values.FindFirst(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase), out collection);
|
=> _customCollections.Values.FindFirst(c => string.Equals(name, c.Identity.Name, StringComparison.OrdinalIgnoreCase), out collection);
|
||||||
|
|
||||||
public bool CollectionById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
|
public bool CollectionById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
|
||||||
=> _customCollections.TryGetValue(id, out collection);
|
=> _customCollections.TryGetValue(id, out collection);
|
||||||
|
|
@ -54,12 +54,12 @@ public class TempCollectionManager : IDisposable, IService
|
||||||
if (GlobalChangeCounter == int.MaxValue)
|
if (GlobalChangeCounter == int.MaxValue)
|
||||||
GlobalChangeCounter = 0;
|
GlobalChangeCounter = 0;
|
||||||
var collection = _storage.CreateTemporary(name, ~Count, GlobalChangeCounter++);
|
var collection = _storage.CreateTemporary(name, ~Count, GlobalChangeCounter++);
|
||||||
Penumbra.Log.Debug($"Creating temporary collection {collection.Name} with {collection.Id}.");
|
Penumbra.Log.Debug($"Creating temporary collection {collection.Identity.Name} with {collection.Identity.Id}.");
|
||||||
if (_customCollections.TryAdd(collection.Id, collection))
|
if (_customCollections.TryAdd(collection.Identity.Id, collection))
|
||||||
{
|
{
|
||||||
// Temporary collection created.
|
// Temporary collection created.
|
||||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, string.Empty);
|
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, string.Empty);
|
||||||
return collection.Id;
|
return collection.Identity.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Guid.Empty;
|
return Guid.Empty;
|
||||||
|
|
@ -74,7 +74,7 @@ public class TempCollectionManager : IDisposable, IService
|
||||||
}
|
}
|
||||||
|
|
||||||
_storage.Delete(collection);
|
_storage.Delete(collection);
|
||||||
Penumbra.Log.Debug($"Deleted temporary collection {collection.Id}.");
|
Penumbra.Log.Debug($"Deleted temporary collection {collection.Identity.Id}.");
|
||||||
GlobalChangeCounter += Math.Max(collection.Counters.Change + 1 - GlobalChangeCounter, 0);
|
GlobalChangeCounter += Math.Max(collection.Counters.Change + 1 - GlobalChangeCounter, 0);
|
||||||
for (var i = 0; i < Collections.Count; ++i)
|
for (var i = 0; i < Collections.Count; ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -83,7 +83,7 @@ public class TempCollectionManager : IDisposable, IService
|
||||||
|
|
||||||
// Temporary collection assignment removed.
|
// Temporary collection assignment removed.
|
||||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, collection, null, Collections[i].DisplayName);
|
_communicator.CollectionChange.Invoke(CollectionType.Temporary, collection, null, Collections[i].DisplayName);
|
||||||
Penumbra.Log.Verbose($"Unassigned temporary collection {collection.Id} from {Collections[i].DisplayName}.");
|
Penumbra.Log.Verbose($"Unassigned temporary collection {collection.Identity.Id} from {Collections[i].DisplayName}.");
|
||||||
Collections.Delete(i--);
|
Collections.Delete(i--);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +96,7 @@ public class TempCollectionManager : IDisposable, IService
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Temporary collection assignment added.
|
// Temporary collection assignment added.
|
||||||
Penumbra.Log.Verbose($"Assigned temporary collection {collection.AnonymizedName} to {Collections.Last().DisplayName}.");
|
Penumbra.Log.Verbose($"Assigned temporary collection {collection.Identity.AnonymizedName} to {Collections.Last().DisplayName}.");
|
||||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, Collections.Last().DisplayName);
|
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, Collections.Last().DisplayName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -127,6 +127,6 @@ public class TempCollectionManager : IDisposable, IService
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var identifier = _actors.CreatePlayer(byteString, worldId);
|
var identifier = _actors.CreatePlayer(byteString, worldId);
|
||||||
return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Id);
|
return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Identity.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,42 +13,21 @@ namespace Penumbra.Collections;
|
||||||
/// - Index is the collections index in the ModCollection.Manager
|
/// - Index is the collections index in the ModCollection.Manager
|
||||||
/// - Settings has the same size as ModManager.Mods.
|
/// - Settings has the same size as ModManager.Mods.
|
||||||
/// - any change in settings or inheritance of the collection causes a Save.
|
/// - any change in settings or inheritance of the collection causes a Save.
|
||||||
/// - the name can not contain invalid path characters and has to be unique when lower-cased.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ModCollection
|
public partial class ModCollection
|
||||||
{
|
{
|
||||||
public const int CurrentVersion = 2;
|
public const int CurrentVersion = 2;
|
||||||
public const string DefaultCollectionName = "Default";
|
|
||||||
public const string EmptyCollectionName = "None";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create the always available Empty Collection that will always sit at index 0,
|
/// Create the always available Empty Collection that will always sit at index 0,
|
||||||
/// can not be deleted and does never create a cache.
|
/// can not be deleted and does never create a cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly ModCollection Empty = new(Guid.Empty, EmptyCollectionName, LocalCollectionId.Zero, 0, 0, CurrentVersion, [], [], []);
|
public static readonly ModCollection Empty = new(ModCollectionIdentity.Empty, 0, CurrentVersion, [], [], []);
|
||||||
|
|
||||||
/// <summary> The name of a collection. </summary>
|
public ModCollectionIdentity Identity;
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
public Guid Id { get; }
|
|
||||||
|
|
||||||
public LocalCollectionId LocalId { get; }
|
|
||||||
|
|
||||||
public string Identifier
|
|
||||||
=> Id.ToString();
|
|
||||||
|
|
||||||
public string ShortIdentifier
|
|
||||||
=> Identifier[..8];
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> Name.Length > 0 ? Name : ShortIdentifier;
|
=> Identity.ToString();
|
||||||
|
|
||||||
/// <summary> Get the first two letters of a collection name and its Index (or None if it is the empty collection). </summary>
|
|
||||||
public string AnonymizedName
|
|
||||||
=> this == Empty ? Empty.Name : Name == DefaultCollectionName ? Name : ShortIdentifier;
|
|
||||||
|
|
||||||
/// <summary> The index of the collection is set and kept up-to-date by the CollectionManager. </summary>
|
|
||||||
public int Index { get; internal set; }
|
|
||||||
|
|
||||||
public CollectionCounters Counters;
|
public CollectionCounters Counters;
|
||||||
|
|
||||||
|
|
@ -90,7 +69,7 @@ public partial class ModCollection
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (Index <= 0)
|
if (Identity.Index <= 0)
|
||||||
return (ModSettings.Empty, this);
|
return (ModSettings.Empty, this);
|
||||||
|
|
||||||
foreach (var collection in GetFlattenedInheritance())
|
foreach (var collection in GetFlattenedInheritance())
|
||||||
|
|
@ -114,17 +93,17 @@ public partial class ModCollection
|
||||||
public ModCollection Duplicate(string name, LocalCollectionId localId, int index)
|
public ModCollection Duplicate(string name, LocalCollectionId localId, int index)
|
||||||
{
|
{
|
||||||
Debug.Assert(index > 0, "Collection duplicated with non-positive index.");
|
Debug.Assert(index > 0, "Collection duplicated with non-positive index.");
|
||||||
return new ModCollection(Guid.NewGuid(), name, localId, index, 0, CurrentVersion, Settings.Select(s => s?.DeepCopy()).ToList(),
|
return new ModCollection(ModCollectionIdentity.New(name, localId, index), 0, CurrentVersion,
|
||||||
[.. DirectlyInheritsFrom], UnusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy()));
|
Settings.Select(s => s?.DeepCopy()).ToList(), [.. DirectlyInheritsFrom],
|
||||||
|
UnusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Constructor for reading from files. </summary>
|
/// <summary> Constructor for reading from files. </summary>
|
||||||
public static ModCollection CreateFromData(SaveService saver, ModStorage mods, Guid id, string name, LocalCollectionId localId, int version,
|
public static ModCollection CreateFromData(SaveService saver, ModStorage mods, ModCollectionIdentity identity, int version,
|
||||||
int index,
|
|
||||||
Dictionary<string, ModSettings.SavedSettings> allSettings, IReadOnlyList<string> inheritances)
|
Dictionary<string, ModSettings.SavedSettings> allSettings, IReadOnlyList<string> inheritances)
|
||||||
{
|
{
|
||||||
Debug.Assert(index > 0, "Collection read with non-positive index.");
|
Debug.Assert(identity.Index > 0, "Collection read with non-positive index.");
|
||||||
var ret = new ModCollection(id, name, localId, index, 0, version, [], [], allSettings)
|
var ret = new ModCollection(identity, 0, version, [], [], allSettings)
|
||||||
{
|
{
|
||||||
InheritanceByName = inheritances,
|
InheritanceByName = inheritances,
|
||||||
};
|
};
|
||||||
|
|
@ -137,7 +116,7 @@ public partial class ModCollection
|
||||||
public static ModCollection CreateTemporary(string name, LocalCollectionId localId, int index, int changeCounter)
|
public static ModCollection CreateTemporary(string name, LocalCollectionId localId, int index, int changeCounter)
|
||||||
{
|
{
|
||||||
Debug.Assert(index < 0, "Temporary collection created with non-negative index.");
|
Debug.Assert(index < 0, "Temporary collection created with non-negative index.");
|
||||||
var ret = new ModCollection(Guid.NewGuid(), name, localId, index, changeCounter, CurrentVersion, [], [], []);
|
var ret = new ModCollection(ModCollectionIdentity.New(name, localId, index), changeCounter, CurrentVersion, [], [], []);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,9 +124,8 @@ public partial class ModCollection
|
||||||
public static ModCollection CreateEmpty(string name, LocalCollectionId localId, int index, int modCount)
|
public static ModCollection CreateEmpty(string name, LocalCollectionId localId, int index, int modCount)
|
||||||
{
|
{
|
||||||
Debug.Assert(index >= 0, "Empty collection created with negative index.");
|
Debug.Assert(index >= 0, "Empty collection created with negative index.");
|
||||||
return new ModCollection(Guid.NewGuid(), name, localId, index, 0, CurrentVersion,
|
return new ModCollection(ModCollectionIdentity.New(name, localId, index), 0, CurrentVersion,
|
||||||
Enumerable.Repeat((ModSettings?)null, modCount).ToList(), [],
|
Enumerable.Repeat((ModSettings?)null, modCount).ToList(), [], []);
|
||||||
[]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Add settings for a new appended mod, by checking if the mod had settings from a previous deletion. </summary>
|
/// <summary> Add settings for a new appended mod, by checking if the mod had settings from a previous deletion. </summary>
|
||||||
|
|
@ -195,16 +173,13 @@ public partial class ModCollection
|
||||||
saver.ImmediateSave(new ModCollectionSave(mods, this));
|
saver.ImmediateSave(new ModCollectionSave(mods, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModCollection(Guid id, string name, LocalCollectionId localId, int index, int changeCounter, int version,
|
private ModCollection(ModCollectionIdentity identity, int changeCounter, int version, List<ModSettings?> appliedSettings,
|
||||||
List<ModSettings?> appliedSettings, List<ModCollection> inheritsFrom, Dictionary<string, ModSettings.SavedSettings> settings)
|
List<ModCollection> inheritsFrom, Dictionary<string, ModSettings.SavedSettings> settings)
|
||||||
{
|
{
|
||||||
Name = name;
|
Identity = identity;
|
||||||
Id = id;
|
|
||||||
LocalId = localId;
|
|
||||||
Index = index;
|
|
||||||
Counters = new CollectionCounters(changeCounter);
|
Counters = new CollectionCounters(changeCounter);
|
||||||
Settings = appliedSettings;
|
Settings = appliedSettings;
|
||||||
UnusedSettings = settings;
|
UnusedSettings = settings;
|
||||||
DirectlyInheritsFrom = inheritsFrom;
|
DirectlyInheritsFrom = inheritsFrom;
|
||||||
foreach (var c in DirectlyInheritsFrom)
|
foreach (var c in DirectlyInheritsFrom)
|
||||||
((List<ModCollection>)c.DirectParentOf).Add(this);
|
((List<ModCollection>)c.DirectParentOf).Add(this);
|
||||||
|
|
|
||||||
42
Penumbra/Collections/ModCollectionIdentity.cs
Normal file
42
Penumbra/Collections/ModCollectionIdentity.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
using OtterGui;
|
||||||
|
using Penumbra.Collections.Manager;
|
||||||
|
|
||||||
|
namespace Penumbra.Collections;
|
||||||
|
|
||||||
|
public struct ModCollectionIdentity(Guid id, LocalCollectionId localId)
|
||||||
|
{
|
||||||
|
public const string DefaultCollectionName = "Default";
|
||||||
|
public const string EmptyCollectionName = "None";
|
||||||
|
|
||||||
|
public static readonly ModCollectionIdentity Empty = new(Guid.Empty, LocalCollectionId.Zero, EmptyCollectionName, 0);
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Guid Id { get; } = id;
|
||||||
|
public LocalCollectionId LocalId { get; } = localId;
|
||||||
|
|
||||||
|
/// <summary> The index of the collection is set and kept up-to-date by the CollectionManager. </summary>
|
||||||
|
public int Index { get; internal set; }
|
||||||
|
|
||||||
|
public string Identifier
|
||||||
|
=> Id.ToString();
|
||||||
|
|
||||||
|
public string ShortIdentifier
|
||||||
|
=> Id.ShortGuid();
|
||||||
|
|
||||||
|
/// <summary> Get the short identifier of a collection unless it is a well-known collection name. </summary>
|
||||||
|
public string AnonymizedName
|
||||||
|
=> Id == Guid.Empty ? EmptyCollectionName : Name == DefaultCollectionName ? Name : ShortIdentifier;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
=> Name.Length > 0 ? Name : ShortIdentifier;
|
||||||
|
|
||||||
|
public ModCollectionIdentity(Guid id, LocalCollectionId localId, string name, int index)
|
||||||
|
: this(id, localId)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ModCollectionIdentity New(string name, LocalCollectionId id, int index)
|
||||||
|
=> new(Guid.NewGuid(), id, name, index);
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
|
||||||
=> fileNames.CollectionFile(modCollection);
|
=> fileNames.CollectionFile(modCollection);
|
||||||
|
|
||||||
public string LogName(string _)
|
public string LogName(string _)
|
||||||
=> modCollection.AnonymizedName;
|
=> modCollection.Identity.AnonymizedName;
|
||||||
|
|
||||||
public string TypeName
|
public string TypeName
|
||||||
=> "Collection";
|
=> "Collection";
|
||||||
|
|
@ -28,10 +28,10 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
|
||||||
j.WriteStartObject();
|
j.WriteStartObject();
|
||||||
j.WritePropertyName("Version");
|
j.WritePropertyName("Version");
|
||||||
j.WriteValue(ModCollection.CurrentVersion);
|
j.WriteValue(ModCollection.CurrentVersion);
|
||||||
j.WritePropertyName(nameof(ModCollection.Id));
|
j.WritePropertyName(nameof(ModCollectionIdentity.Id));
|
||||||
j.WriteValue(modCollection.Identifier);
|
j.WriteValue(modCollection.Identity.Identifier);
|
||||||
j.WritePropertyName(nameof(ModCollection.Name));
|
j.WritePropertyName(nameof(ModCollectionIdentity.Name));
|
||||||
j.WriteValue(modCollection.Name);
|
j.WriteValue(modCollection.Identity.Name);
|
||||||
j.WritePropertyName(nameof(ModCollection.Settings));
|
j.WritePropertyName(nameof(ModCollection.Settings));
|
||||||
|
|
||||||
// Write all used and unused settings by mod directory name.
|
// Write all used and unused settings by mod directory name.
|
||||||
|
|
@ -57,7 +57,7 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
|
||||||
|
|
||||||
// Inherit by collection name.
|
// Inherit by collection name.
|
||||||
j.WritePropertyName("Inheritance");
|
j.WritePropertyName("Inheritance");
|
||||||
x.Serialize(j, modCollection.InheritanceByName ?? modCollection.DirectlyInheritsFrom.Select(c => c.Identifier));
|
x.Serialize(j, modCollection.InheritanceByName ?? modCollection.DirectlyInheritsFrom.Select(c => c.Identity.Identifier));
|
||||||
j.WriteEndObject();
|
j.WriteEndObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,8 +79,8 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
|
||||||
{
|
{
|
||||||
var obj = JObject.Parse(File.ReadAllText(file.FullName));
|
var obj = JObject.Parse(File.ReadAllText(file.FullName));
|
||||||
version = obj["Version"]?.ToObject<int>() ?? 0;
|
version = obj["Version"]?.ToObject<int>() ?? 0;
|
||||||
name = obj[nameof(ModCollection.Name)]?.ToObject<string>() ?? string.Empty;
|
name = obj[nameof(ModCollectionIdentity.Name)]?.ToObject<string>() ?? string.Empty;
|
||||||
id = obj[nameof(ModCollection.Id)]?.ToObject<Guid>() ?? (version == 1 ? Guid.NewGuid() : Guid.Empty);
|
id = obj[nameof(ModCollectionIdentity.Id)]?.ToObject<Guid>() ?? (version == 1 ? Guid.NewGuid() : Guid.Empty);
|
||||||
// Custom deserialization that is converted with the constructor.
|
// Custom deserialization that is converted with the constructor.
|
||||||
settings = obj[nameof(ModCollection.Settings)]?.ToObject<Dictionary<string, ModSettings.SavedSettings>>() ?? settings;
|
settings = obj[nameof(ModCollection.Settings)]?.ToObject<Dictionary<string, ModSettings.SavedSettings>>() ?? settings;
|
||||||
inheritance = obj["Inheritance"]?.ToObject<List<string>>() ?? inheritance;
|
inheritance = obj["Inheritance"]?.ToObject<List<string>>() ?? inheritance;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public readonly struct ResolveData(ModCollection collection, nint gameObject)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
=> ModCollection.Name;
|
=> ModCollection.Identity.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ResolveDataExtensions
|
public static class ResolveDataExtensions
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
{
|
{
|
||||||
_chat.Print(collection == null
|
_chat.Print(collection == null
|
||||||
? $"The {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}" : string.Empty)} is already unassigned"
|
? $"The {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}" : string.Empty)} is already unassigned"
|
||||||
: $"{collection.Name} already is the {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}." : ".")}");
|
: $"{collection.Identity.Name} already is the {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}." : ".")}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,13 +363,13 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
}
|
}
|
||||||
|
|
||||||
Print(
|
Print(
|
||||||
$"Removed {oldCollection.Name} as {type.ToName()} Collection assignment {(identifier.IsValid ? $" for {identifier}." : ".")}");
|
$"Removed {oldCollection.Identity.Name} as {type.ToName()} Collection assignment {(identifier.IsValid ? $" for {identifier}." : ".")}");
|
||||||
anySuccess = true;
|
anySuccess = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_collectionManager.Active.SetCollection(collection!, type, individualIndex);
|
_collectionManager.Active.SetCollection(collection!, type, individualIndex);
|
||||||
Print($"Assigned {collection!.Name} as {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}." : ".")}");
|
Print($"Assigned {collection!.Identity.Name} as {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}." : ".")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return anySuccess;
|
return anySuccess;
|
||||||
|
|
@ -440,7 +440,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
|
|
||||||
_chat.Print(new SeStringBuilder().AddText("Mod ").AddPurple(mod.Name, true)
|
_chat.Print(new SeStringBuilder().AddText("Mod ").AddPurple(mod.Name, true)
|
||||||
.AddText("already had the desired state in collection ")
|
.AddText("already had the desired state in collection ")
|
||||||
.AddYellow(collection!.Name, true).AddText(".").BuiltString);
|
.AddYellow(collection!.Identity.Name, true).AddText(".").BuiltString);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -458,7 +458,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
_collectionEditor.SetModSetting(collection!, mod, groupIndex, setting);
|
_collectionEditor.SetModSetting(collection!, mod, groupIndex, setting);
|
||||||
Print(() => new SeStringBuilder().AddText("Changed settings of group ").AddGreen(groupName, true).AddText(" in mod ")
|
Print(() => new SeStringBuilder().AddText("Changed settings of group ").AddGreen(groupName, true).AddText(" in mod ")
|
||||||
.AddPurple(mod.Name, true).AddText(" in collection ")
|
.AddPurple(mod.Name, true).AddText(" in collection ")
|
||||||
.AddYellow(collection!.Name, true).AddText(".").BuiltString);
|
.AddYellow(collection!.Identity.Name, true).AddText(".").BuiltString);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -543,7 +543,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
changes |= HandleModState(state, collection!, mod);
|
changes |= HandleModState(state, collection!, mod);
|
||||||
|
|
||||||
if (!changes)
|
if (!changes)
|
||||||
Print(() => new SeStringBuilder().AddText("No mod states were changed in collection ").AddYellow(collection!.Name, true)
|
Print(() => new SeStringBuilder().AddText("No mod states were changed in collection ").AddYellow(collection!.Identity.Name, true)
|
||||||
.AddText(".").BuiltString);
|
.AddText(".").BuiltString);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -558,7 +558,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
collection = string.Equals(lowerName, ModCollection.Empty.Name, StringComparison.OrdinalIgnoreCase)
|
collection = string.Equals(lowerName, ModCollection.Empty.Identity.Name, StringComparison.OrdinalIgnoreCase)
|
||||||
? ModCollection.Empty
|
? ModCollection.Empty
|
||||||
: _collectionManager.Storage.ByIdentifier(lowerName, out var c)
|
: _collectionManager.Storage.ByIdentifier(lowerName, out var c)
|
||||||
? c
|
? c
|
||||||
|
|
@ -614,7 +614,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Print(() => new SeStringBuilder().AddText("Enabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
|
Print(() => new SeStringBuilder().AddText("Enabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
|
||||||
.AddYellow(collection.Name, true)
|
.AddYellow(collection.Identity.Name, true)
|
||||||
.AddText(".").BuiltString);
|
.AddText(".").BuiltString);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
@ -623,7 +623,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Print(() => new SeStringBuilder().AddText("Disabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
|
Print(() => new SeStringBuilder().AddText("Disabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
|
||||||
.AddYellow(collection.Name, true)
|
.AddYellow(collection.Identity.Name, true)
|
||||||
.AddText(".").BuiltString);
|
.AddText(".").BuiltString);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
@ -634,7 +634,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
|
|
||||||
Print(() => new SeStringBuilder().AddText(setting ? "Enabled mod " : "Disabled mod ").AddPurple(mod.Name, true)
|
Print(() => new SeStringBuilder().AddText(setting ? "Enabled mod " : "Disabled mod ").AddPurple(mod.Name, true)
|
||||||
.AddText(" in collection ")
|
.AddText(" in collection ")
|
||||||
.AddYellow(collection.Name, true)
|
.AddYellow(collection.Identity.Name, true)
|
||||||
.AddText(".").BuiltString);
|
.AddText(".").BuiltString);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
@ -643,7 +643,7 @@ public class CommandHandler : IDisposable, IApiService
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Print(() => new SeStringBuilder().AddText("Set mod ").AddPurple(mod.Name, true).AddText(" in collection ")
|
Print(() => new SeStringBuilder().AddText("Set mod ").AddPurple(mod.Name, true).AddText(" in collection ")
|
||||||
.AddYellow(collection.Name, true)
|
.AddYellow(collection.Identity.Name, true)
|
||||||
.AddText(" to inherit.").BuiltString);
|
.AddText(" to inherit.").BuiltString);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public unsafe class AtchCallerHook1 : FastHook<AtchCallerHook1.Delegate>, IDispo
|
||||||
Task.Result.Original(data, slot, unk, playerModel);
|
Task.Result.Original(data, slot, unk, playerModel);
|
||||||
_metaState.AtchCollection.Pop();
|
_metaState.AtchCollection.Pop();
|
||||||
Penumbra.Log.Excessive(
|
Penumbra.Log.Excessive(
|
||||||
$"[AtchCaller1] Invoked on 0x{(ulong)data:X} with {slot}, {unk:X}, 0x{playerModel.Address:X}, identified to {collection.ModCollection.AnonymizedName}.");
|
$"[AtchCaller1] Invoked on 0x{(ulong)data:X} with {slot}, {unk:X}, 0x{playerModel.Address:X}, identified to {collection.ModCollection.Identity.AnonymizedName}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public unsafe class AtchCallerHook2 : FastHook<AtchCallerHook2.Delegate>, IDispo
|
||||||
Task.Result.Original(data, slot, unk, playerModel, unk2);
|
Task.Result.Original(data, slot, unk, playerModel, unk2);
|
||||||
_metaState.AtchCollection.Pop();
|
_metaState.AtchCollection.Pop();
|
||||||
Penumbra.Log.Excessive(
|
Penumbra.Log.Excessive(
|
||||||
$"[AtchCaller2] Invoked on 0x{(ulong)data:X} with {slot}, {unk:X}, 0x{playerModel.Address:X}, {unk2}, identified to {collection.ModCollection.AnonymizedName}.");
|
$"[AtchCaller2] Invoked on 0x{(ulong)data:X} with {slot}, {unk:X}, 0x{playerModel.Address:X}, {unk2}, identified to {collection.ModCollection.Identity.AnonymizedName}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ public sealed unsafe class MetaState : IDisposable, IService
|
||||||
_lastCreatedCollection = _collectionResolver.IdentifyLastGameObjectCollection(true);
|
_lastCreatedCollection = _collectionResolver.IdentifyLastGameObjectCollection(true);
|
||||||
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero)
|
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero)
|
||||||
_communicator.CreatingCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
|
_communicator.CreatingCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
|
||||||
_lastCreatedCollection.ModCollection.Id, (nint)modelCharaId, (nint)customize, (nint)equipData);
|
_lastCreatedCollection.ModCollection.Identity.Id, (nint)modelCharaId, (nint)customize, (nint)equipData);
|
||||||
|
|
||||||
var decal = new DecalReverter(Config, _characterUtility, _resources, _lastCreatedCollection,
|
var decal = new DecalReverter(Config, _characterUtility, _resources, _lastCreatedCollection,
|
||||||
UsesDecal(*(uint*)modelCharaId, (nint)customize));
|
UsesDecal(*(uint*)modelCharaId, (nint)customize));
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ public static class PathDataHandler
|
||||||
/// <summary> Create the encoding path for an IMC file. </summary>
|
/// <summary> Create the encoding path for an IMC file. </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static FullPath CreateImc(CiByteString path, ModCollection collection)
|
public static FullPath CreateImc(CiByteString path, ModCollection collection)
|
||||||
=> new($"|{collection.LocalId.Id}_{collection.Counters.Imc}_{DiscriminatorString}|{path}");
|
=> new($"|{collection.Identity.LocalId.Id}_{collection.Counters.Imc}_{DiscriminatorString}|{path}");
|
||||||
|
|
||||||
/// <summary> Create the encoding path for a TMB file. </summary>
|
/// <summary> Create the encoding path for a TMB file. </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
@ -47,17 +47,17 @@ public static class PathDataHandler
|
||||||
/// <summary> Create the encoding path for an ATCH file. </summary>
|
/// <summary> Create the encoding path for an ATCH file. </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static FullPath CreateAtch(CiByteString path, ModCollection collection)
|
public static FullPath CreateAtch(CiByteString path, ModCollection collection)
|
||||||
=> new($"|{collection.LocalId.Id}_{collection.Counters.Atch}_{DiscriminatorString}|{path}");
|
=> new($"|{collection.Identity.LocalId.Id}_{collection.Counters.Atch}_{DiscriminatorString}|{path}");
|
||||||
|
|
||||||
/// <summary> Create the encoding path for a MTRL file. </summary>
|
/// <summary> Create the encoding path for a MTRL file. </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static FullPath CreateMtrl(CiByteString path, ModCollection collection, Utf8GamePath originalPath)
|
public static FullPath CreateMtrl(CiByteString path, ModCollection collection, Utf8GamePath originalPath)
|
||||||
=> new($"|{collection.LocalId.Id}_{collection.Counters.Change}_{originalPath.Path.Crc32:X8}_{DiscriminatorString}|{path}");
|
=> new($"|{collection.Identity.LocalId.Id}_{collection.Counters.Change}_{originalPath.Path.Crc32:X8}_{DiscriminatorString}|{path}");
|
||||||
|
|
||||||
/// <summary> The base function shared by most file types. </summary>
|
/// <summary> The base function shared by most file types. </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static FullPath CreateBase(CiByteString path, ModCollection collection)
|
private static FullPath CreateBase(CiByteString path, ModCollection collection)
|
||||||
=> new($"|{collection.LocalId.Id}_{collection.Counters.Change}_{DiscriminatorString}|{path}");
|
=> new($"|{collection.Identity.LocalId.Id}_{collection.Counters.Change}_{DiscriminatorString}|{path}");
|
||||||
|
|
||||||
/// <summary> Read an additional data blurb and parse it into usable data for all file types but Materials. </summary>
|
/// <summary> Read an additional data blurb and parse it into usable data for all file types but Materials. </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,6 @@ public sealed class ImcFilePostProcessor(CollectionStorage collections) : IFileP
|
||||||
|
|
||||||
file.Replace(resource);
|
file.Replace(resource);
|
||||||
Penumbra.Log.Verbose(
|
Penumbra.Log.Verbose(
|
||||||
$"[ResourceLoader] Loaded {originalGamePath} from file and replaced with IMC from collection {collection.AnonymizedName}.");
|
$"[ResourceLoader] Loaded {originalGamePath} from file and replaced with IMC from collection {collection.Identity.AnonymizedName}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ public class ResourceTreeFactory(
|
||||||
var (name, anonymizedName, related) = GetCharacterName(character);
|
var (name, anonymizedName, related) = GetCharacterName(character);
|
||||||
var networked = character.EntityId != 0xE0000000;
|
var networked = character.EntityId != 0xE0000000;
|
||||||
var tree = new ResourceTree(name, anonymizedName, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related,
|
var tree = new ResourceTree(name, anonymizedName, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related,
|
||||||
networked, collectionResolveData.ModCollection.Name, collectionResolveData.ModCollection.AnonymizedName);
|
networked, collectionResolveData.ModCollection.Identity.Name, collectionResolveData.ModCollection.Identity.AnonymizedName);
|
||||||
var globalContext = new GlobalResolveContext(metaFileManager, objectIdentifier, collectionResolveData.ModCollection,
|
var globalContext = new GlobalResolveContext(metaFileManager, objectIdentifier, collectionResolveData.ModCollection,
|
||||||
cache, (flags & Flags.WithUiData) != 0);
|
cache, (flags & Flags.WithUiData) != 0);
|
||||||
using (var _ = pathState.EnterInternalResolve())
|
using (var _ = pathState.EnterInternalResolve())
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,10 @@ public class TemporaryMod : IMod
|
||||||
DirectoryInfo? dir = null;
|
DirectoryInfo? dir = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Name, config.ReplaceNonAsciiOnImport, true);
|
dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Identity.Name, config.ReplaceNonAsciiOnImport, true);
|
||||||
var fileDir = Directory.CreateDirectory(Path.Combine(dir.FullName, "files"));
|
var fileDir = Directory.CreateDirectory(Path.Combine(dir.FullName, "files"));
|
||||||
modManager.DataEditor.CreateMeta(dir, collection.Name, character ?? config.DefaultModAuthor,
|
modManager.DataEditor.CreateMeta(dir, collection.Identity.Name, character ?? config.DefaultModAuthor,
|
||||||
$"Mod generated from temporary collection {collection.Id} for {character ?? "Unknown Character"} with name {collection.Name}.",
|
$"Mod generated from temporary collection {collection.Identity.Id} for {character ?? "Unknown Character"} with name {collection.Identity.Name}.",
|
||||||
null, null);
|
null, null);
|
||||||
var mod = new Mod(dir);
|
var mod = new Mod(dir);
|
||||||
var defaultMod = mod.Default;
|
var defaultMod = mod.Default;
|
||||||
|
|
@ -99,11 +99,11 @@ public class TemporaryMod : IMod
|
||||||
saveService.ImmediateSaveSync(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport));
|
saveService.ImmediateSaveSync(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport));
|
||||||
modManager.AddMod(dir, false);
|
modManager.AddMod(dir, false);
|
||||||
Penumbra.Log.Information(
|
Penumbra.Log.Information(
|
||||||
$"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Identifier}.");
|
$"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Identity.Identifier}.");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Penumbra.Log.Error($"Could not save temporary collection {collection.Identifier} to permanent Mod:\n{e}");
|
Penumbra.Log.Error($"Could not save temporary collection {collection.Identity.Identifier} to permanent Mod:\n{e}");
|
||||||
if (dir != null && Directory.Exists(dir.FullName))
|
if (dir != null && Directory.Exists(dir.FullName))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -245,24 +245,24 @@ public class Penumbra : IDalamudPlugin
|
||||||
|
|
||||||
void PrintCollection(ModCollection c, CollectionCache _)
|
void PrintCollection(ModCollection c, CollectionCache _)
|
||||||
=> sb.Append(
|
=> sb.Append(
|
||||||
$"> **`Collection {c.AnonymizedName + ':',-18}`** Inheritances: `{c.DirectlyInheritsFrom.Count,3}`, Enabled Mods: `{c.ActualSettings.Count(s => s is { Enabled: true }),4}`, Conflicts: `{c.AllConflicts.SelectMany(x => x).Sum(x => x is { HasPriority: true, Solved: true } ? x.Conflicts.Count : 0),5}/{c.AllConflicts.SelectMany(x => x).Sum(x => x.HasPriority ? x.Conflicts.Count : 0),5}`\n");
|
$"> **`Collection {c.Identity.AnonymizedName + ':',-18}`** Inheritances: `{c.DirectlyInheritsFrom.Count,3}`, Enabled Mods: `{c.ActualSettings.Count(s => s is { Enabled: true }),4}`, Conflicts: `{c.AllConflicts.SelectMany(x => x).Sum(x => x is { HasPriority: true, Solved: true } ? x.Conflicts.Count : 0),5}/{c.AllConflicts.SelectMany(x => x).Sum(x => x.HasPriority ? x.Conflicts.Count : 0),5}`\n");
|
||||||
|
|
||||||
sb.AppendLine("**Collections**");
|
sb.AppendLine("**Collections**");
|
||||||
sb.Append($"> **`#Collections: `** {_collectionManager.Storage.Count - 1}\n");
|
sb.Append($"> **`#Collections: `** {_collectionManager.Storage.Count - 1}\n");
|
||||||
sb.Append($"> **`#Temp Collections: `** {_tempCollections.Count}\n");
|
sb.Append($"> **`#Temp Collections: `** {_tempCollections.Count}\n");
|
||||||
sb.Append($"> **`Active Collections: `** {_collectionManager.Caches.Count}\n");
|
sb.Append($"> **`Active Collections: `** {_collectionManager.Caches.Count}\n");
|
||||||
sb.Append($"> **`Base Collection: `** {_collectionManager.Active.Default.AnonymizedName}\n");
|
sb.Append($"> **`Base Collection: `** {_collectionManager.Active.Default.Identity.AnonymizedName}\n");
|
||||||
sb.Append($"> **`Interface Collection: `** {_collectionManager.Active.Interface.AnonymizedName}\n");
|
sb.Append($"> **`Interface Collection: `** {_collectionManager.Active.Interface.Identity.AnonymizedName}\n");
|
||||||
sb.Append($"> **`Selected Collection: `** {_collectionManager.Active.Current.AnonymizedName}\n");
|
sb.Append($"> **`Selected Collection: `** {_collectionManager.Active.Current.Identity.AnonymizedName}\n");
|
||||||
foreach (var (type, name, _) in CollectionTypeExtensions.Special)
|
foreach (var (type, name, _) in CollectionTypeExtensions.Special)
|
||||||
{
|
{
|
||||||
var collection = _collectionManager.Active.ByType(type);
|
var collection = _collectionManager.Active.ByType(type);
|
||||||
if (collection != null)
|
if (collection != null)
|
||||||
sb.Append($"> **`{name,-29}`** {collection.AnonymizedName}\n");
|
sb.Append($"> **`{name,-29}`** {collection.Identity.AnonymizedName}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (name, id, collection) in _collectionManager.Active.Individuals.Assignments)
|
foreach (var (name, id, collection) in _collectionManager.Active.Individuals.Assignments)
|
||||||
sb.Append($"> **`{id[0].Incognito(name) + ':',-29}`** {collection.AnonymizedName}\n");
|
sb.Append($"> **`{id[0].Incognito(name) + ':',-29}`** {collection.Identity.AnonymizedName}\n");
|
||||||
|
|
||||||
foreach (var collection in _collectionManager.Caches.Active)
|
foreach (var collection in _collectionManager.Caches.Active)
|
||||||
PrintCollection(collection, collection._cache!);
|
PrintCollection(collection, collection._cache!);
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
|
||||||
private Configuration _config = null!;
|
private Configuration _config = null!;
|
||||||
private JObject _data = null!;
|
private JObject _data = null!;
|
||||||
|
|
||||||
public string CurrentCollection = ModCollection.DefaultCollectionName;
|
public string CurrentCollection = ModCollectionIdentity.DefaultCollectionName;
|
||||||
public string DefaultCollection = ModCollection.DefaultCollectionName;
|
public string DefaultCollection = ModCollectionIdentity.DefaultCollectionName;
|
||||||
public string ForcedCollection = string.Empty;
|
public string ForcedCollection = string.Empty;
|
||||||
public Dictionary<string, string> CharacterCollections = [];
|
public Dictionary<string, string> CharacterCollections = [];
|
||||||
public Dictionary<string, string> ModSortOrder = [];
|
public Dictionary<string, string> ModSortOrder = [];
|
||||||
|
|
@ -346,7 +346,7 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
|
||||||
if (!collectionJson.Exists)
|
if (!collectionJson.Exists)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var defaultCollectionFile = new FileInfo(saveService.FileNames.CollectionFile(ModCollection.DefaultCollectionName));
|
var defaultCollectionFile = new FileInfo(saveService.FileNames.CollectionFile(ModCollectionIdentity.DefaultCollectionName));
|
||||||
if (defaultCollectionFile.Exists)
|
if (defaultCollectionFile.Exists)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -380,7 +380,7 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
|
||||||
|
|
||||||
var emptyStorage = new ModStorage();
|
var emptyStorage = new ModStorage();
|
||||||
// Only used for saving and immediately discarded, so the local collection id here is irrelevant.
|
// Only used for saving and immediately discarded, so the local collection id here is irrelevant.
|
||||||
var collection = ModCollection.CreateFromData(saveService, emptyStorage, Guid.NewGuid(), ModCollection.DefaultCollectionName, LocalCollectionId.Zero, 0, 1, dict, []);
|
var collection = ModCollection.CreateFromData(saveService, emptyStorage, ModCollectionIdentity.New(ModCollectionIdentity.DefaultCollectionName, LocalCollectionId.Zero, 1), 0, dict, []);
|
||||||
saveService.ImmediateSaveSync(new ModCollectionSave(emptyStorage, collection));
|
saveService.ImmediateSaveSync(new ModCollectionSave(emptyStorage, collection));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ public sealed class CrashHandlerService : IDisposable, IService
|
||||||
var name = GetActorName(character);
|
var name = GetActorName(character);
|
||||||
lock (_eventWriter)
|
lock (_eventWriter)
|
||||||
{
|
{
|
||||||
_eventWriter?.AnimationFuncInvoked.WriteLine(character, name.Span, collection.Id, type);
|
_eventWriter?.AnimationFuncInvoked.WriteLine(character, name.Span, collection.Identity.Id, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -293,7 +293,7 @@ public sealed class CrashHandlerService : IDisposable, IService
|
||||||
var name = GetActorName(resolveData.AssociatedGameObject);
|
var name = GetActorName(resolveData.AssociatedGameObject);
|
||||||
lock (_eventWriter)
|
lock (_eventWriter)
|
||||||
{
|
{
|
||||||
_eventWriter!.FileLoaded.WriteLine(resolveData.AssociatedGameObject, name.Span, resolveData.ModCollection.Id,
|
_eventWriter!.FileLoaded.WriteLine(resolveData.AssociatedGameObject, name.Span, resolveData.ModCollection.Identity.Id,
|
||||||
manipulatedPath.Value.InternalName.Span, originalPath.Path.Span);
|
manipulatedPath.Value.InternalName.Span, originalPath.Path.Span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ public class FilenameService(IDalamudPluginInterface pi) : IService
|
||||||
|
|
||||||
/// <summary> Obtain the path of a collection file given its name.</summary>
|
/// <summary> Obtain the path of a collection file given its name.</summary>
|
||||||
public string CollectionFile(ModCollection collection)
|
public string CollectionFile(ModCollection collection)
|
||||||
=> CollectionFile(collection.Identifier);
|
=> CollectionFile(collection.Identity.Identifier);
|
||||||
|
|
||||||
/// <summary> Obtain the path of a collection file given its name. </summary>
|
/// <summary> Obtain the path of a collection file given its name. </summary>
|
||||||
public string CollectionFile(string collectionName)
|
public string CollectionFile(string collectionName)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public class CollectionSelectHeader : IUiService
|
||||||
_selection = selection;
|
_selection = selection;
|
||||||
_resolver = resolver;
|
_resolver = resolver;
|
||||||
_activeCollections = collectionManager.Active;
|
_activeCollections = collectionManager.Active;
|
||||||
_collectionCombo = new CollectionCombo(collectionManager, () => collectionManager.Storage.OrderBy(c => c.Name).ToList());
|
_collectionCombo = new CollectionCombo(collectionManager, () => collectionManager.Storage.OrderBy(c => c.Identity.Name).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Draw the header line that can quick switch between collections. </summary>
|
/// <summary> Draw the header line that can quick switch between collections. </summary>
|
||||||
|
|
@ -77,10 +77,10 @@ public class CollectionSelectHeader : IUiService
|
||||||
return CheckCollection(collection) switch
|
return CheckCollection(collection) switch
|
||||||
{
|
{
|
||||||
CollectionState.Empty => (collection, "None", "The base collection is configured to use no mods.", true),
|
CollectionState.Empty => (collection, "None", "The base collection is configured to use no mods.", true),
|
||||||
CollectionState.Selected => (collection, collection.Name,
|
CollectionState.Selected => (collection, collection.Identity.Name,
|
||||||
"The configured base collection is already selected as the current collection.", true),
|
"The configured base collection is already selected as the current collection.", true),
|
||||||
CollectionState.Available => (collection, collection.Name,
|
CollectionState.Available => (collection, collection.Identity.Name,
|
||||||
$"Select the configured base collection {collection.Name} as the current collection.", false),
|
$"Select the configured base collection {collection.Identity.Name} as the current collection.", false),
|
||||||
_ => throw new Exception("Can not happen."),
|
_ => throw new Exception("Can not happen."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -91,10 +91,11 @@ public class CollectionSelectHeader : IUiService
|
||||||
return CheckCollection(collection) switch
|
return CheckCollection(collection) switch
|
||||||
{
|
{
|
||||||
CollectionState.Empty => (collection, "None", "The loaded player character is configured to use no mods.", true),
|
CollectionState.Empty => (collection, "None", "The loaded player character is configured to use no mods.", true),
|
||||||
CollectionState.Selected => (collection, collection.Name,
|
CollectionState.Selected => (collection, collection.Identity.Name,
|
||||||
"The collection configured to apply to the loaded player character is already selected as the current collection.", true),
|
"The collection configured to apply to the loaded player character is already selected as the current collection.", true),
|
||||||
CollectionState.Available => (collection, collection.Name,
|
CollectionState.Available => (collection, collection.Identity.Name,
|
||||||
$"Select the collection {collection.Name} that applies to the loaded player character as the current collection.", false),
|
$"Select the collection {collection.Identity.Name} that applies to the loaded player character as the current collection.",
|
||||||
|
false),
|
||||||
_ => throw new Exception("Can not happen."),
|
_ => throw new Exception("Can not happen."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -105,10 +106,10 @@ public class CollectionSelectHeader : IUiService
|
||||||
return CheckCollection(collection) switch
|
return CheckCollection(collection) switch
|
||||||
{
|
{
|
||||||
CollectionState.Empty => (collection, "None", "The interface collection is configured to use no mods.", true),
|
CollectionState.Empty => (collection, "None", "The interface collection is configured to use no mods.", true),
|
||||||
CollectionState.Selected => (collection, collection.Name,
|
CollectionState.Selected => (collection, collection.Identity.Name,
|
||||||
"The configured interface collection is already selected as the current collection.", true),
|
"The configured interface collection is already selected as the current collection.", true),
|
||||||
CollectionState.Available => (collection, collection.Name,
|
CollectionState.Available => (collection, collection.Identity.Name,
|
||||||
$"Select the configured interface collection {collection.Name} as the current collection.", false),
|
$"Select the configured interface collection {collection.Identity.Name} as the current collection.", false),
|
||||||
_ => throw new Exception("Can not happen."),
|
_ => throw new Exception("Can not happen."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -120,8 +121,8 @@ public class CollectionSelectHeader : IUiService
|
||||||
{
|
{
|
||||||
CollectionState.Unavailable => (null, "Not Inherited",
|
CollectionState.Unavailable => (null, "Not Inherited",
|
||||||
"The settings of the selected mod are not inherited from another collection.", true),
|
"The settings of the selected mod are not inherited from another collection.", true),
|
||||||
CollectionState.Available => (collection, collection!.Name,
|
CollectionState.Available => (collection, collection!.Identity.Name,
|
||||||
$"Select the collection {collection!.Name} from which the selected mod inherits its settings as the current collection.",
|
$"Select the collection {collection!.Identity.Name} from which the selected mod inherits its settings as the current collection.",
|
||||||
false),
|
false),
|
||||||
_ => throw new Exception("Can not happen."),
|
_ => throw new Exception("Can not happen."),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,13 @@ public sealed class CollectionCombo(CollectionManager manager, Func<IReadOnlyLis
|
||||||
}
|
}
|
||||||
|
|
||||||
_color.Push(ImGuiCol.FrameBg, color).Push(ImGuiCol.FrameBgHovered, color);
|
_color.Push(ImGuiCol.FrameBg, color).Push(ImGuiCol.FrameBgHovered, color);
|
||||||
if (Draw(label, current.Name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing()) && CurrentSelection != null)
|
if (Draw(label, current.Identity.Name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing()) && CurrentSelection != null)
|
||||||
manager.Active.SetCollection(CurrentSelection, CollectionType.Current);
|
manager.Active.SetCollection(CurrentSelection, CollectionType.Current);
|
||||||
_color.Dispose();
|
_color.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string ToString(ModCollection obj)
|
protected override string ToString(ModCollection obj)
|
||||||
=> obj.Name;
|
=> obj.Identity.Name;
|
||||||
|
|
||||||
protected override void DrawCombo(string label, string preview, string tooltip, int currentSelected, float previewWidth, float itemHeight,
|
protected override void DrawCombo(string label, string preview, string tooltip, int currentSelected, float previewWidth, float itemHeight,
|
||||||
ImGuiComboFlags flags)
|
ImGuiComboFlags flags)
|
||||||
|
|
|
||||||
|
|
@ -221,16 +221,16 @@ public sealed class CollectionPanel(
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.BeginGroup();
|
ImGui.BeginGroup();
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f));
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f));
|
||||||
var name = _newName ?? collection.Name;
|
var name = _newName ?? collection.Identity.Name;
|
||||||
var identifier = collection.Identifier;
|
var identifier = collection.Identity.Identifier;
|
||||||
var width = ImGui.GetContentRegionAvail().X;
|
var width = ImGui.GetContentRegionAvail().X;
|
||||||
var fileName = saveService.FileNames.CollectionFile(collection);
|
var fileName = saveService.FileNames.CollectionFile(collection);
|
||||||
ImGui.SetNextItemWidth(width);
|
ImGui.SetNextItemWidth(width);
|
||||||
if (ImGui.InputText("##name", ref name, 128))
|
if (ImGui.InputText("##name", ref name, 128))
|
||||||
_newName = name;
|
_newName = name;
|
||||||
if (ImGui.IsItemDeactivatedAfterEdit() && _newName != null && _newName != collection.Name)
|
if (ImGui.IsItemDeactivatedAfterEdit() && _newName != null && _newName != collection.Identity.Name)
|
||||||
{
|
{
|
||||||
collection.Name = _newName;
|
collection.Identity.Name = _newName;
|
||||||
saveService.QueueSave(new ModCollectionSave(mods, collection));
|
saveService.QueueSave(new ModCollectionSave(mods, collection));
|
||||||
selector.RestoreCollections();
|
selector.RestoreCollections();
|
||||||
_newName = null;
|
_newName = null;
|
||||||
|
|
@ -242,7 +242,7 @@ public sealed class CollectionPanel(
|
||||||
|
|
||||||
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
using (ImRaii.PushFont(UiBuilder.MonoFont))
|
||||||
{
|
{
|
||||||
if (ImGui.Button(collection.Identifier, new Vector2(width, 0)))
|
if (ImGui.Button(collection.Identity.Identifier, new Vector2(width, 0)))
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
|
||||||
|
|
@ -289,9 +289,9 @@ public sealed class CollectionPanel(
|
||||||
_active.SetCollection(null, type, _active.Individuals.GetGroup(identifier));
|
_active.SetCollection(null, type, _active.Individuals.GetGroup(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var coll in _collections.OrderBy(c => c.Name))
|
foreach (var coll in _collections.OrderBy(c => c.Identity.Name))
|
||||||
{
|
{
|
||||||
if (coll != collection && ImGui.MenuItem($"Use {coll.Name}."))
|
if (coll != collection && ImGui.MenuItem($"Use {coll.Identity.Name}."))
|
||||||
_active.SetCollection(coll, type, _active.Individuals.GetGroup(identifier));
|
_active.SetCollection(coll, type, _active.Individuals.GetGroup(identifier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -418,7 +418,7 @@ public sealed class CollectionPanel(
|
||||||
private string Name(ModCollection? collection)
|
private string Name(ModCollection? collection)
|
||||||
=> collection == null ? "Unassigned" :
|
=> collection == null ? "Unassigned" :
|
||||||
collection == ModCollection.Empty ? "Use No Mods" :
|
collection == ModCollection.Empty ? "Use No Mods" :
|
||||||
incognito.IncognitoMode ? collection.AnonymizedName : collection.Name;
|
incognito.IncognitoMode ? collection.Identity.AnonymizedName : collection.Identity.Name;
|
||||||
|
|
||||||
private void DrawIndividualButton(string intro, Vector2 width, string tooltip, char suffix, params ActorIdentifier[] identifiers)
|
private void DrawIndividualButton(string intro, Vector2 width, string tooltip, char suffix, params ActorIdentifier[] identifiers)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool Filtered(int idx)
|
protected override bool Filtered(int idx)
|
||||||
=> !Items[idx].Name.Contains(Filter, StringComparison.OrdinalIgnoreCase);
|
=> !Items[idx].Identity.Name.Contains(Filter, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
private const string PayloadString = "Collection";
|
private const string PayloadString = "Collection";
|
||||||
|
|
||||||
|
|
@ -111,12 +111,12 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Name(ModCollection collection)
|
private string Name(ModCollection collection)
|
||||||
=> _incognito.IncognitoMode || collection.Name.Length == 0 ? collection.AnonymizedName : collection.Name;
|
=> _incognito.IncognitoMode || collection.Identity.Name.Length == 0 ? collection.Identity.AnonymizedName : collection.Identity.Name;
|
||||||
|
|
||||||
public void RestoreCollections()
|
public void RestoreCollections()
|
||||||
{
|
{
|
||||||
Items.Clear();
|
Items.Clear();
|
||||||
foreach (var c in _storage.OrderBy(c => c.Name))
|
foreach (var c in _storage.OrderBy(c => c.Identity.Name))
|
||||||
Items.Add(c);
|
Items.Add(c);
|
||||||
SetFilterDirty();
|
SetFilterDirty();
|
||||||
SetCurrent(_active.Current);
|
SetCurrent(_active.Current);
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DrawInheritedChildren(ModCollection collection)
|
private void DrawInheritedChildren(ModCollection collection)
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId(collection.Index);
|
using var id = ImRaii.PushId(collection.Identity.Index);
|
||||||
using var indent = ImRaii.PushIndent();
|
using var indent = ImRaii.PushIndent();
|
||||||
|
|
||||||
// Get start point for the lines (top of the selector).
|
// Get start point for the lines (top of the selector).
|
||||||
|
|
@ -114,7 +114,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
|
||||||
_seenInheritedCollections.Contains(inheritance));
|
_seenInheritedCollections.Contains(inheritance));
|
||||||
_seenInheritedCollections.Add(inheritance);
|
_seenInheritedCollections.Add(inheritance);
|
||||||
|
|
||||||
ImRaii.TreeNode($"{Name(inheritance)}###{inheritance.Id}",
|
ImRaii.TreeNode($"{Name(inheritance)}###{inheritance.Identity.Id}",
|
||||||
ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
|
ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
|
||||||
var (minRect, maxRect) = (ImGui.GetItemRectMin(), ImGui.GetItemRectMax());
|
var (minRect, maxRect) = (ImGui.GetItemRectMin(), ImGui.GetItemRectMax());
|
||||||
DrawInheritanceTreeClicks(inheritance, false);
|
DrawInheritanceTreeClicks(inheritance, false);
|
||||||
|
|
@ -140,7 +140,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.HandledConflictMod.Value(),
|
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.HandledConflictMod.Value(),
|
||||||
_seenInheritedCollections.Contains(collection));
|
_seenInheritedCollections.Contains(collection));
|
||||||
_seenInheritedCollections.Add(collection);
|
_seenInheritedCollections.Add(collection);
|
||||||
using var tree = ImRaii.TreeNode($"{Name(collection)}###{collection.Name}", ImGuiTreeNodeFlags.NoTreePushOnOpen);
|
using var tree = ImRaii.TreeNode($"{Name(collection)}###{collection.Identity.Name}", ImGuiTreeNodeFlags.NoTreePushOnOpen);
|
||||||
color.Pop();
|
color.Pop();
|
||||||
DrawInheritanceTreeClicks(collection, true);
|
DrawInheritanceTreeClicks(collection, true);
|
||||||
DrawInheritanceDropSource(collection);
|
DrawInheritanceDropSource(collection);
|
||||||
|
|
@ -252,7 +252,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
|
||||||
|
|
||||||
foreach (var collection in _collections
|
foreach (var collection in _collections
|
||||||
.Where(c => InheritanceManager.CheckValidInheritance(_active.Current, c) == InheritanceManager.ValidInheritance.Valid)
|
.Where(c => InheritanceManager.CheckValidInheritance(_active.Current, c) == InheritanceManager.ValidInheritance.Valid)
|
||||||
.OrderBy(c => c.Name))
|
.OrderBy(c => c.Identity.Name))
|
||||||
{
|
{
|
||||||
if (ImGui.Selectable(Name(collection), _newInheritance == collection))
|
if (ImGui.Selectable(Name(collection), _newInheritance == collection))
|
||||||
_newInheritance = collection;
|
_newInheritance = collection;
|
||||||
|
|
@ -312,5 +312,5 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
|
||||||
}
|
}
|
||||||
|
|
||||||
private string Name(ModCollection collection)
|
private string Name(ModCollection collection)
|
||||||
=> incognito.IncognitoMode ? collection.AnonymizedName : collection.Name;
|
=> incognito.IncognitoMode ? collection.Identity.AnonymizedName : collection.Identity.Name;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
|
||||||
foreach (var ((collection, parent, color, state), idx) in _cache.WithIndex())
|
foreach (var ((collection, parent, color, state), idx) in _cache.WithIndex())
|
||||||
{
|
{
|
||||||
using var id = ImUtf8.PushId(idx);
|
using var id = ImUtf8.PushId(idx);
|
||||||
ImUtf8.DrawTableColumn(collection.Name);
|
ImUtf8.DrawTableColumn(collection.Identity.Name);
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImUtf8.Text(ToText(state), color);
|
ImUtf8.Text(ToText(state), color);
|
||||||
|
|
@ -65,7 +65,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
|
||||||
{
|
{
|
||||||
if (context)
|
if (context)
|
||||||
{
|
{
|
||||||
ImUtf8.Text(collection.Name);
|
ImUtf8.Text(collection.Identity.Name);
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
using (ImRaii.Disabled(state is ModState.Enabled && parent == collection))
|
using (ImRaii.Disabled(state is ModState.Enabled && parent == collection))
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +95,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImUtf8.DrawTableColumn(parent == collection ? string.Empty : parent.Name);
|
ImUtf8.DrawTableColumn(parent == collection ? string.Empty : parent.Identity.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ public class ModPanelSettingsTab(
|
||||||
|
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.PressEnterWarningBg);
|
using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.PressEnterWarningBg);
|
||||||
var width = new Vector2(ImGui.GetContentRegionAvail().X, 0);
|
var width = new Vector2(ImGui.GetContentRegionAvail().X, 0);
|
||||||
if (ImGui.Button($"These settings are inherited from {selection.Collection.Name}.", width))
|
if (ImGui.Button($"These settings are inherited from {selection.Collection.Identity.Name}.", width))
|
||||||
collectionManager.Editor.SetModInheritance(collectionManager.Active.Current, selection.Mod!, false);
|
collectionManager.Editor.SetModInheritance(collectionManager.Active.Current, selection.Mod!, false);
|
||||||
|
|
||||||
ImGuiUtil.HoverTooltip("You can click this button to copy the current settings to the current selection.\n"
|
ImGuiUtil.HoverTooltip("You can click this button to copy the current settings to the current selection.\n"
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ public sealed class ResourceWatcher : IDisposable, ITab, IUiService
|
||||||
{
|
{
|
||||||
var pathString = manipulatedPath != null ? $"custom file {name2} instead of {name}" : name;
|
var pathString = manipulatedPath != null ? $"custom file {name2} instead of {name}" : name;
|
||||||
Penumbra.Log.Information(
|
Penumbra.Log.Information(
|
||||||
$"[ResourceLoader] [LOAD] [{handle->FileType}] Loaded {pathString} to 0x{(ulong)handle:X} using collection {data.ModCollection.AnonymizedName} for {Name(data, "no associated object.")} (Refcount {handle->RefCount}) ");
|
$"[ResourceLoader] [LOAD] [{handle->FileType}] Loaded {pathString} to 0x{(ulong)handle:X} using collection {data.ModCollection.Identity.AnonymizedName} for {Name(data, "no associated object.")} (Refcount {handle->RefCount}) ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ internal sealed class ResourceWatcherTable : Table<Record>
|
||||||
=> 80 * UiHelpers.Scale;
|
=> 80 * UiHelpers.Scale;
|
||||||
|
|
||||||
public override string ToName(Record item)
|
public override string ToName(Record item)
|
||||||
=> item.Collection?.Name ?? string.Empty;
|
=> (item.Collection != null ? item.Collection.Identity.Name : null) ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ObjectColumn : ColumnString<Record>
|
private sealed class ObjectColumn : ColumnString<Record>
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
if (collection.HasCache)
|
if (collection.HasCache)
|
||||||
{
|
{
|
||||||
using var color = PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value());
|
using var color = PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value());
|
||||||
using var node = TreeNode($"{collection.Name} (Change Counter {collection.Counters.Change})###{collection.Name}");
|
using var node = TreeNode($"{collection.Identity.Name} (Change Counter {collection.Counters.Change})###{collection.Identity.Name}");
|
||||||
if (!node)
|
if (!node)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -239,7 +239,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using var color = PushColor(ImGuiCol.Text, ColorId.UndefinedMod.Value());
|
using var color = PushColor(ImGuiCol.Text, ColorId.UndefinedMod.Value());
|
||||||
TreeNode($"{collection.AnonymizedName} (Change Counter {collection.Counters.Change})",
|
TreeNode($"{collection.Identity.AnonymizedName} (Change Counter {collection.Counters.Change})",
|
||||||
ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
|
ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,9 +265,9 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
{
|
{
|
||||||
PrintValue("Penumbra Version", $"{_validityChecker.Version} {DebugVersionString}");
|
PrintValue("Penumbra Version", $"{_validityChecker.Version} {DebugVersionString}");
|
||||||
PrintValue("Git Commit Hash", _validityChecker.CommitHash);
|
PrintValue("Git Commit Hash", _validityChecker.CommitHash);
|
||||||
PrintValue(TutorialService.SelectedCollection, _collectionManager.Active.Current.Name);
|
PrintValue(TutorialService.SelectedCollection, _collectionManager.Active.Current.Identity.Name);
|
||||||
PrintValue(" has Cache", _collectionManager.Active.Current.HasCache.ToString());
|
PrintValue(" has Cache", _collectionManager.Active.Current.HasCache.ToString());
|
||||||
PrintValue(TutorialService.DefaultCollection, _collectionManager.Active.Default.Name);
|
PrintValue(TutorialService.DefaultCollection, _collectionManager.Active.Default.Identity.Name);
|
||||||
PrintValue(" has Cache", _collectionManager.Active.Default.HasCache.ToString());
|
PrintValue(" has Cache", _collectionManager.Active.Default.HasCache.ToString());
|
||||||
PrintValue("Mod Manager BasePath", _modManager.BasePath.Name);
|
PrintValue("Mod Manager BasePath", _modManager.BasePath.Name);
|
||||||
PrintValue("Mod Manager BasePath-Full", _modManager.BasePath.FullName);
|
PrintValue("Mod Manager BasePath-Full", _modManager.BasePath.FullName);
|
||||||
|
|
@ -518,7 +518,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ImGui.TextUnformatted(
|
ImGui.TextUnformatted(
|
||||||
$"Last Game Object: 0x{_collectionResolver.IdentifyLastGameObjectCollection(true).AssociatedGameObject:X} ({_collectionResolver.IdentifyLastGameObjectCollection(true).ModCollection.Name})");
|
$"Last Game Object: 0x{_collectionResolver.IdentifyLastGameObjectCollection(true).AssociatedGameObject:X} ({_collectionResolver.IdentifyLastGameObjectCollection(true).ModCollection.Identity.Name})");
|
||||||
using (var drawTree = TreeNode("Draw Object to Object"))
|
using (var drawTree = TreeNode("Draw Object to Object"))
|
||||||
{
|
{
|
||||||
if (drawTree)
|
if (drawTree)
|
||||||
|
|
@ -545,7 +545,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
ImGui.TextUnformatted(name);
|
ImGui.TextUnformatted(name);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
var collection = _collectionResolver.IdentifyCollection(gameObject, true);
|
var collection = _collectionResolver.IdentifyCollection(gameObject, true);
|
||||||
ImGui.TextUnformatted(collection.ModCollection.Name);
|
ImGui.TextUnformatted(collection.ModCollection.Identity.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -561,7 +561,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted($"{data.AssociatedGameObject:X}");
|
ImGui.TextUnformatted($"{data.AssociatedGameObject:X}");
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(data.ModCollection.Name);
|
ImGui.TextUnformatted(data.ModCollection.Identity.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -574,12 +574,12 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
if (table)
|
if (table)
|
||||||
{
|
{
|
||||||
ImGuiUtil.DrawTableColumn("Current Mtrl Data");
|
ImGuiUtil.DrawTableColumn("Current Mtrl Data");
|
||||||
ImGuiUtil.DrawTableColumn(_subfileHelper.MtrlData.ModCollection.Name);
|
ImGuiUtil.DrawTableColumn(_subfileHelper.MtrlData.ModCollection.Identity.Name);
|
||||||
ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.MtrlData.AssociatedGameObject:X}");
|
ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.MtrlData.AssociatedGameObject:X}");
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
||||||
ImGuiUtil.DrawTableColumn("Current Avfx Data");
|
ImGuiUtil.DrawTableColumn("Current Avfx Data");
|
||||||
ImGuiUtil.DrawTableColumn(_subfileHelper.AvfxData.ModCollection.Name);
|
ImGuiUtil.DrawTableColumn(_subfileHelper.AvfxData.ModCollection.Identity.Name);
|
||||||
ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.AvfxData.AssociatedGameObject:X}");
|
ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.AvfxData.AssociatedGameObject:X}");
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
||||||
|
|
@ -591,7 +591,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
foreach (var (resource, resolve) in _subfileHelper)
|
foreach (var (resource, resolve) in _subfileHelper)
|
||||||
{
|
{
|
||||||
ImGuiUtil.DrawTableColumn($"0x{resource:X}");
|
ImGuiUtil.DrawTableColumn($"0x{resource:X}");
|
||||||
ImGuiUtil.DrawTableColumn(resolve.ModCollection.Name);
|
ImGuiUtil.DrawTableColumn(resolve.ModCollection.Identity.Name);
|
||||||
ImGuiUtil.DrawTableColumn($"0x{resolve.AssociatedGameObject:X}");
|
ImGuiUtil.DrawTableColumn($"0x{resolve.AssociatedGameObject:X}");
|
||||||
ImGuiUtil.DrawTableColumn($"{((ResourceHandle*)resource)->FileName()}");
|
ImGuiUtil.DrawTableColumn($"{((ResourceHandle*)resource)->FileName()}");
|
||||||
}
|
}
|
||||||
|
|
@ -611,7 +611,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
ImGuiUtil.DrawTableColumn($"{((GameObject*)address)->ObjectIndex}");
|
ImGuiUtil.DrawTableColumn($"{((GameObject*)address)->ObjectIndex}");
|
||||||
ImGuiUtil.DrawTableColumn($"0x{address:X}");
|
ImGuiUtil.DrawTableColumn($"0x{address:X}");
|
||||||
ImGuiUtil.DrawTableColumn(identifier.ToString());
|
ImGuiUtil.DrawTableColumn(identifier.ToString());
|
||||||
ImGuiUtil.DrawTableColumn(collection.Name);
|
ImGuiUtil.DrawTableColumn(collection.Identity.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,12 +77,12 @@ public class ModsTab(
|
||||||
{
|
{
|
||||||
Penumbra.Log.Error($"Exception thrown during ModPanel Render:\n{e}");
|
Penumbra.Log.Error($"Exception thrown during ModPanel Render:\n{e}");
|
||||||
Penumbra.Log.Error($"{modManager.Count} Mods\n"
|
Penumbra.Log.Error($"{modManager.Count} Mods\n"
|
||||||
+ $"{_activeCollections.Current.AnonymizedName} Current Collection\n"
|
+ $"{_activeCollections.Current.Identity.AnonymizedName} Current Collection\n"
|
||||||
+ $"{_activeCollections.Current.Settings.Count} Settings\n"
|
+ $"{_activeCollections.Current.Settings.Count} Settings\n"
|
||||||
+ $"{selector.SortMode.Name} Sort Mode\n"
|
+ $"{selector.SortMode.Name} Sort Mode\n"
|
||||||
+ $"{selector.SelectedLeaf?.Name ?? "NULL"} Selected Leaf\n"
|
+ $"{selector.SelectedLeaf?.Name ?? "NULL"} Selected Leaf\n"
|
||||||
+ $"{selector.Selected?.Name ?? "NULL"} Selected Mod\n"
|
+ $"{selector.Selected?.Name ?? "NULL"} Selected Mod\n"
|
||||||
+ $"{string.Join(", ", _activeCollections.Current.DirectlyInheritsFrom.Select(c => c.AnonymizedName))} Inheritances\n");
|
+ $"{string.Join(", ", _activeCollections.Current.DirectlyInheritsFrom.Select(c => c.Identity.AnonymizedName))} Inheritances\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,14 +83,14 @@ public class TutorialService : IUiService
|
||||||
+ "Go here after setting up your root folder to continue the tutorial!")
|
+ "Go here after setting up your root folder to continue the tutorial!")
|
||||||
.Register("Initial Setup, Step 4: Managing Collections",
|
.Register("Initial Setup, Step 4: Managing Collections",
|
||||||
"On the left, we have the collection selector. Here, we can create new collections - either empty ones or by duplicating existing ones - and delete any collections not needed anymore.\n"
|
"On the left, we have the collection selector. Here, we can create new collections - either empty ones or by duplicating existing ones - and delete any collections not needed anymore.\n"
|
||||||
+ $"There will always be one collection called {ModCollection.DefaultCollectionName} that can not be deleted.")
|
+ $"There will always be one collection called {ModCollectionIdentity.DefaultCollectionName} that can not be deleted.")
|
||||||
.Register($"Initial Setup, Step 5: {SelectedCollection}",
|
.Register($"Initial Setup, Step 5: {SelectedCollection}",
|
||||||
$"The {SelectedCollection} is the one we highlighted in the selector. It is the collection we are currently looking at and editing.\nAny changes we make in our mod settings later in the next tab will edit this collection.\n"
|
$"The {SelectedCollection} is the one we highlighted in the selector. It is the collection we are currently looking at and editing.\nAny changes we make in our mod settings later in the next tab will edit this collection.\n"
|
||||||
+ $"We should already have the collection named {ModCollection.DefaultCollectionName} selected, and for our simple setup, we do not need to do anything here.\n\n")
|
+ $"We should already have the collection named {ModCollectionIdentity.DefaultCollectionName} selected, and for our simple setup, we do not need to do anything here.\n\n")
|
||||||
.Register("Initial Setup, Step 6: Simple Assignments",
|
.Register("Initial Setup, Step 6: Simple Assignments",
|
||||||
"Aside from being a collection of settings, we can also assign collections to different functions. This is used to make different mods apply to different characters.\n"
|
"Aside from being a collection of settings, we can also assign collections to different functions. This is used to make different mods apply to different characters.\n"
|
||||||
+ "The Simple Assignments panel shows you the possible assignments that are enough for most people along with descriptions.\n"
|
+ "The Simple Assignments panel shows you the possible assignments that are enough for most people along with descriptions.\n"
|
||||||
+ $"If you are just starting, you can see that the {ModCollection.DefaultCollectionName} is currently assigned to {CollectionType.Default.ToName()} and {CollectionType.Interface.ToName()}.\n"
|
+ $"If you are just starting, you can see that the {ModCollectionIdentity.DefaultCollectionName} is currently assigned to {CollectionType.Default.ToName()} and {CollectionType.Interface.ToName()}.\n"
|
||||||
+ "You can also assign 'Use No Mods' instead of a collection by clicking on the function buttons.")
|
+ "You can also assign 'Use No Mods' instead of a collection by clicking on the function buttons.")
|
||||||
.Register("Individual Assignments",
|
.Register("Individual Assignments",
|
||||||
"In the Individual Assignments panel, you can manually create assignments for very specific characters or monsters, not just yourself or ones you can currently target.")
|
"In the Individual Assignments panel, you can manually create assignments for very specific characters or monsters, not just yourself or ones you can currently target.")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue