mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Fix some bugs and start work on new collections tab.
This commit is contained in:
parent
e9fc57022e
commit
fba5bc6820
26 changed files with 1346 additions and 717 deletions
|
|
@ -221,6 +221,16 @@ public static class RaceEnumExtensions
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ToShortName(this SubRace subRace)
|
||||||
|
{
|
||||||
|
return subRace switch
|
||||||
|
{
|
||||||
|
SubRace.SeekerOfTheSun => "Sunseeker",
|
||||||
|
SubRace.KeeperOfTheMoon => "Moonkeeper",
|
||||||
|
_ => subRace.ToName(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static bool FitsRace(this SubRace subRace, Race race)
|
public static bool FitsRace(this SubRace subRace, Race race)
|
||||||
=> subRace.ToRace() == race;
|
=> subRace.ToRace() == race;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public class CollectionCacheManager : IDisposable
|
||||||
MetaFileManager = metaFileManager;
|
MetaFileManager = metaFileManager;
|
||||||
_active = active;
|
_active = active;
|
||||||
|
|
||||||
_communicator.CollectionChange.Subscribe(OnCollectionChange);
|
_communicator.CollectionChange.Subscribe(OnCollectionChange, -100);
|
||||||
_communicator.ModPathChanged.Subscribe(OnModChangeAddition, -100);
|
_communicator.ModPathChanged.Subscribe(OnModChangeAddition, -100);
|
||||||
_communicator.ModPathChanged.Subscribe(OnModChangeRemoval, 100);
|
_communicator.ModPathChanged.Subscribe(OnModChangeRemoval, 100);
|
||||||
_communicator.TemporaryGlobalModChange.Subscribe(OnGlobalModChange);
|
_communicator.TemporaryGlobalModChange.Subscribe(OnGlobalModChange);
|
||||||
|
|
|
||||||
|
|
@ -13,24 +13,35 @@ using Penumbra.Util;
|
||||||
|
|
||||||
namespace Penumbra.Collections.Manager;
|
namespace Penumbra.Collections.Manager;
|
||||||
|
|
||||||
|
public class ActiveCollectionData
|
||||||
|
{
|
||||||
|
public ModCollection Current { get; internal set; } = ModCollection.Empty;
|
||||||
|
public ModCollection Default { get; internal set; } = ModCollection.Empty;
|
||||||
|
public ModCollection Interface { get; internal set; } = ModCollection.Empty;
|
||||||
|
|
||||||
|
public readonly ModCollection?[] SpecialCollections = new ModCollection?[Enum.GetValues<Api.Enums.ApiCollectionType>().Length - 3];
|
||||||
|
}
|
||||||
|
|
||||||
public class ActiveCollections : ISavable, IDisposable
|
public class ActiveCollections : ISavable, IDisposable
|
||||||
{
|
{
|
||||||
public const int Version = 1;
|
public const int Version = 1;
|
||||||
|
|
||||||
private readonly CollectionStorage _storage;
|
private readonly CollectionStorage _storage;
|
||||||
private readonly CommunicatorService _communicator;
|
private readonly CommunicatorService _communicator;
|
||||||
private readonly SaveService _saveService;
|
private readonly SaveService _saveService;
|
||||||
|
private readonly ActiveCollectionData _data;
|
||||||
|
|
||||||
public ActiveCollections(Configuration config, CollectionStorage storage, ActorService actors, CommunicatorService communicator, SaveService saveService)
|
public ActiveCollections(Configuration config, CollectionStorage storage, ActorService actors, CommunicatorService communicator, SaveService saveService, ActiveCollectionData data)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_storage = storage;
|
||||||
_communicator = communicator;
|
_communicator = communicator;
|
||||||
_saveService = saveService;
|
_saveService = saveService;
|
||||||
|
_data = data;
|
||||||
Current = storage.DefaultNamed;
|
Current = storage.DefaultNamed;
|
||||||
Default = storage.DefaultNamed;
|
Default = storage.DefaultNamed;
|
||||||
Interface = storage.DefaultNamed;
|
Interface = storage.DefaultNamed;
|
||||||
Individuals = new IndividualCollections(actors.AwaitedService, config);
|
Individuals = new IndividualCollections(actors.AwaitedService, config);
|
||||||
_communicator.CollectionChange.Subscribe(OnCollectionChange);
|
_communicator.CollectionChange.Subscribe(OnCollectionChange, -100);
|
||||||
LoadCollections();
|
LoadCollections();
|
||||||
UpdateCurrentCollectionInUse();
|
UpdateCurrentCollectionInUse();
|
||||||
}
|
}
|
||||||
|
|
@ -39,16 +50,28 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
=> _communicator.CollectionChange.Unsubscribe(OnCollectionChange);
|
=> _communicator.CollectionChange.Unsubscribe(OnCollectionChange);
|
||||||
|
|
||||||
/// <summary> The collection currently selected for changing settings. </summary>
|
/// <summary> The collection currently selected for changing settings. </summary>
|
||||||
public ModCollection Current { get; private set; }
|
public ModCollection Current
|
||||||
|
{
|
||||||
|
get => _data.Current;
|
||||||
|
private set => _data.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Whether the currently selected collection is used either directly via assignment or via inheritance. </summary>
|
/// <summary> Whether the currently selected collection is used either directly via assignment or via inheritance. </summary>
|
||||||
public bool CurrentCollectionInUse { get; private set; }
|
public bool CurrentCollectionInUse { get; private set; }
|
||||||
|
|
||||||
/// <summary> The collection used for general file redirections and all characters not specifically named. </summary>
|
/// <summary> The collection used for general file redirections and all characters not specifically named. </summary>
|
||||||
public ModCollection Default { get; private set; }
|
public ModCollection Default
|
||||||
|
{
|
||||||
|
get => _data.Default;
|
||||||
|
private set => _data.Default = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> The collection used for all files categorized as UI files. </summary>
|
/// <summary> The collection used for all files categorized as UI files. </summary>
|
||||||
public ModCollection Interface { get; private set; }
|
public ModCollection Interface
|
||||||
|
{
|
||||||
|
get => _data.Interface;
|
||||||
|
private set => _data.Interface = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> The list of individual assignments. </summary>
|
/// <summary> The list of individual assignments. </summary>
|
||||||
public readonly IndividualCollections Individuals;
|
public readonly IndividualCollections Individuals;
|
||||||
|
|
@ -58,16 +81,17 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
=> Individuals.TryGetCollection(identifier, out var c) ? c : Default;
|
=> Individuals.TryGetCollection(identifier, out var c) ? c : Default;
|
||||||
|
|
||||||
/// <summary> The list of group assignments. </summary>
|
/// <summary> The list of group assignments. </summary>
|
||||||
private readonly ModCollection?[] _specialCollections = new ModCollection?[Enum.GetValues<Api.Enums.ApiCollectionType>().Length - 3];
|
private ModCollection?[] SpecialCollections
|
||||||
|
=> _data.SpecialCollections;
|
||||||
|
|
||||||
/// <summary> Return all actually assigned group assignments. </summary>
|
/// <summary> Return all actually assigned group assignments. </summary>
|
||||||
public IEnumerable<KeyValuePair<CollectionType, ModCollection>> SpecialAssignments
|
public IEnumerable<KeyValuePair<CollectionType, ModCollection>> SpecialAssignments
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
for (var i = 0; i < _specialCollections.Length; ++i)
|
for (var i = 0; i < SpecialCollections.Length; ++i)
|
||||||
{
|
{
|
||||||
var collection = _specialCollections[i];
|
var collection = SpecialCollections[i];
|
||||||
if (collection != null)
|
if (collection != null)
|
||||||
yield return new KeyValuePair<CollectionType, ModCollection>((CollectionType)i, collection);
|
yield return new KeyValuePair<CollectionType, ModCollection>((CollectionType)i, collection);
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +106,7 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
public ModCollection? ByType(CollectionType type, ActorIdentifier identifier)
|
public ModCollection? ByType(CollectionType type, ActorIdentifier identifier)
|
||||||
{
|
{
|
||||||
if (type.IsSpecial())
|
if (type.IsSpecial())
|
||||||
return _specialCollections[(int)type];
|
return SpecialCollections[(int)type];
|
||||||
|
|
||||||
return type switch
|
return type switch
|
||||||
{
|
{
|
||||||
|
|
@ -94,13 +118,13 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Create a special collection if it does not exist and set it to Empty. </summary>
|
/// <summary> Create a special collection if it does not exist and set it to the current default. </summary>
|
||||||
public bool CreateSpecialCollection(CollectionType collectionType)
|
public bool CreateSpecialCollection(CollectionType collectionType)
|
||||||
{
|
{
|
||||||
if (!collectionType.IsSpecial() || _specialCollections[(int)collectionType] != null)
|
if (!collectionType.IsSpecial() || SpecialCollections[(int)collectionType] != null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_specialCollections[(int)collectionType] = Default;
|
SpecialCollections[(int)collectionType] = Default;
|
||||||
_communicator.CollectionChange.Invoke(collectionType, null, Default, string.Empty);
|
_communicator.CollectionChange.Invoke(collectionType, null, Default, string.Empty);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -111,11 +135,11 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
if (!collectionType.IsSpecial())
|
if (!collectionType.IsSpecial())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var old = _specialCollections[(int)collectionType];
|
var old = SpecialCollections[(int)collectionType];
|
||||||
if (old == null)
|
if (old == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_specialCollections[(int)collectionType] = null;
|
SpecialCollections[(int)collectionType] = null;
|
||||||
_communicator.CollectionChange.Invoke(collectionType, old, null, string.Empty);
|
_communicator.CollectionChange.Invoke(collectionType, old, null, string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,7 +168,38 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
_saveService.QueueSave(this);
|
_saveService.QueueSave(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Set a active collection, can be used to set Default, Current, Interface, Special, or Individual collections. </summary>
|
/// <summary> Set and create an active collection, can be used to set Default, Current, Interface, Special, or Individual collections. </summary>
|
||||||
|
public void SetCollection(ModCollection? collection, CollectionType collectionType, ActorIdentifier[] identifiers)
|
||||||
|
{
|
||||||
|
if (collectionType is CollectionType.Individual && identifiers.Length > 0 && identifiers[0].IsValid)
|
||||||
|
{
|
||||||
|
var idx = Individuals.Index(identifiers[0]);
|
||||||
|
if (idx >= 0)
|
||||||
|
{
|
||||||
|
if (collection == null)
|
||||||
|
RemoveIndividualCollection(idx);
|
||||||
|
else
|
||||||
|
SetCollection(collection, collectionType, idx);
|
||||||
|
}
|
||||||
|
else if (collection != null)
|
||||||
|
{
|
||||||
|
CreateIndividualCollection(identifiers);
|
||||||
|
SetCollection(collection, CollectionType.Individual, Individuals.Count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (collection == null)
|
||||||
|
RemoveSpecialCollection(collectionType);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CreateSpecialCollection(collectionType);
|
||||||
|
SetCollection(collection, collectionType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Set an active collection, can be used to set Default, Current, Interface, Special, or Individual collections. </summary>
|
||||||
public void SetCollection(ModCollection collection, CollectionType collectionType, int individualIndex = -1)
|
public void SetCollection(ModCollection collection, CollectionType collectionType, int individualIndex = -1)
|
||||||
{
|
{
|
||||||
var oldCollection = collectionType switch
|
var oldCollection = collectionType switch
|
||||||
|
|
@ -154,7 +209,7 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
CollectionType.Current => Current,
|
CollectionType.Current => Current,
|
||||||
CollectionType.Individual when individualIndex >= 0 && individualIndex < Individuals.Count => Individuals[individualIndex].Collection,
|
CollectionType.Individual when individualIndex >= 0 && individualIndex < Individuals.Count => Individuals[individualIndex].Collection,
|
||||||
CollectionType.Individual => null,
|
CollectionType.Individual => null,
|
||||||
_ when collectionType.IsSpecial() => _specialCollections[(int)collectionType] ?? Default,
|
_ when collectionType.IsSpecial() => SpecialCollections[(int)collectionType] ?? Default,
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -178,7 +233,7 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
_specialCollections[(int)collectionType] = collection;
|
SpecialCollections[(int)collectionType] = collection;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,7 +260,7 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
{ nameof(Interface), Interface.Name },
|
{ nameof(Interface), Interface.Name },
|
||||||
{ nameof(Current), Current.Name },
|
{ nameof(Current), Current.Name },
|
||||||
};
|
};
|
||||||
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.Name);
|
jObj.Add(type.ToString(), collection.Name);
|
||||||
|
|
||||||
|
|
@ -215,7 +270,7 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateCurrentCollectionInUse()
|
private void UpdateCurrentCollectionInUse()
|
||||||
=> CurrentCollectionInUse = _specialCollections
|
=> CurrentCollectionInUse = SpecialCollections
|
||||||
.OfType<ModCollection>()
|
.OfType<ModCollection>()
|
||||||
.Prepend(Interface)
|
.Prepend(Interface)
|
||||||
.Prepend(Default)
|
.Prepend(Default)
|
||||||
|
|
@ -240,9 +295,9 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
if (oldCollection == Current)
|
if (oldCollection == Current)
|
||||||
SetCollection(Default.Index > ModCollection.Empty.Index ? Default : _storage.DefaultNamed, CollectionType.Current);
|
SetCollection(Default.Index > ModCollection.Empty.Index ? Default : _storage.DefaultNamed, CollectionType.Current);
|
||||||
|
|
||||||
for (var i = 0; i < _specialCollections.Length; ++i)
|
for (var i = 0; i < SpecialCollections.Length; ++i)
|
||||||
{
|
{
|
||||||
if (oldCollection == _specialCollections[i])
|
if (oldCollection == SpecialCollections[i])
|
||||||
SetCollection(ModCollection.Empty, (CollectionType)i);
|
SetCollection(ModCollection.Empty, (CollectionType)i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,7 +384,7 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_specialCollections[(int)type] = typeCollection;
|
SpecialCollections[(int)type] = typeCollection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -398,24 +453,6 @@ public class ActiveCollections : ISavable, IDisposable
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
// The group of all Characters is redundant if they are all equal to Default or unassigned.
|
|
||||||
case CollectionType.MalePlayerCharacter:
|
|
||||||
case CollectionType.MaleNonPlayerCharacter:
|
|
||||||
case CollectionType.FemalePlayerCharacter:
|
|
||||||
case CollectionType.FemaleNonPlayerCharacter:
|
|
||||||
var first = ByType(CollectionType.MalePlayerCharacter) ?? Default;
|
|
||||||
var second = ByType(CollectionType.MaleNonPlayerCharacter) ?? Default;
|
|
||||||
var third = ByType(CollectionType.FemalePlayerCharacter) ?? Default;
|
|
||||||
var fourth = ByType(CollectionType.FemaleNonPlayerCharacter) ?? Default;
|
|
||||||
if (first.Index == second.Index
|
|
||||||
&& first.Index == third.Index
|
|
||||||
&& first.Index == fourth.Index
|
|
||||||
&& first.Index == Default.Index)
|
|
||||||
return
|
|
||||||
"Assignment is currently redundant due to the group [Male, Female, Player, NPC] Characters being unassigned or identical to each other and Default.\n"
|
|
||||||
+ "You can keep just the Default Assignment.";
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
// Children and Elderly are redundant if they are identical to both Male NPCs and Female NPCs, or if they are unassigned to Default.
|
// Children and Elderly are redundant if they are identical to both Male NPCs and Female NPCs, or if they are unassigned to Default.
|
||||||
case CollectionType.NonPlayerChild:
|
case CollectionType.NonPlayerChild:
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newCollection = duplicate?.Duplicate(name, _collections.Count) ?? ModCollection.CreateEmpty(name, _collections.Count);
|
var newCollection = duplicate?.Duplicate(name, _collections.Count) ?? ModCollection.CreateEmpty(name, _collections.Count, _modStorage.Count);
|
||||||
_collections.Add(newCollection);
|
_collections.Add(newCollection);
|
||||||
|
|
||||||
_saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection));
|
_saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection));
|
||||||
|
|
@ -200,7 +200,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
|
||||||
/// Does not check for uniqueness.
|
/// Does not check for uniqueness.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static bool IsValidName(string name)
|
private static bool IsValidName(string name)
|
||||||
=> name.Length > 0 && name.All(c => !c.IsInvalidAscii() && c is not '|' && !c.IsInvalidInPath());
|
=> name.Length is > 0 and < 32 && name.All(c => !c.IsInvalidAscii() && c is not '|' && !c.IsInvalidInPath());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read all collection files in the Collection Directory.
|
/// Read all collection files in the Collection Directory.
|
||||||
|
|
|
||||||
|
|
@ -10,96 +10,96 @@ public enum CollectionType : byte
|
||||||
// Special Collections
|
// Special Collections
|
||||||
Yourself = Api.Enums.ApiCollectionType.Yourself,
|
Yourself = Api.Enums.ApiCollectionType.Yourself,
|
||||||
|
|
||||||
MalePlayerCharacter = Api.Enums.ApiCollectionType.MalePlayerCharacter,
|
MalePlayerCharacter = Api.Enums.ApiCollectionType.MalePlayerCharacter,
|
||||||
FemalePlayerCharacter = Api.Enums.ApiCollectionType.FemalePlayerCharacter,
|
FemalePlayerCharacter = Api.Enums.ApiCollectionType.FemalePlayerCharacter,
|
||||||
MaleNonPlayerCharacter = Api.Enums.ApiCollectionType.MaleNonPlayerCharacter,
|
MaleNonPlayerCharacter = Api.Enums.ApiCollectionType.MaleNonPlayerCharacter,
|
||||||
FemaleNonPlayerCharacter = Api.Enums.ApiCollectionType.FemaleNonPlayerCharacter,
|
FemaleNonPlayerCharacter = Api.Enums.ApiCollectionType.FemaleNonPlayerCharacter,
|
||||||
NonPlayerChild = Api.Enums.ApiCollectionType.NonPlayerChild,
|
NonPlayerChild = Api.Enums.ApiCollectionType.NonPlayerChild,
|
||||||
NonPlayerElderly = Api.Enums.ApiCollectionType.NonPlayerElderly,
|
NonPlayerElderly = Api.Enums.ApiCollectionType.NonPlayerElderly,
|
||||||
|
|
||||||
MaleMidlander = Api.Enums.ApiCollectionType.MaleMidlander,
|
MaleMidlander = Api.Enums.ApiCollectionType.MaleMidlander,
|
||||||
FemaleMidlander = Api.Enums.ApiCollectionType.FemaleMidlander,
|
FemaleMidlander = Api.Enums.ApiCollectionType.FemaleMidlander,
|
||||||
MaleHighlander = Api.Enums.ApiCollectionType.MaleHighlander,
|
MaleHighlander = Api.Enums.ApiCollectionType.MaleHighlander,
|
||||||
FemaleHighlander = Api.Enums.ApiCollectionType.FemaleHighlander,
|
FemaleHighlander = Api.Enums.ApiCollectionType.FemaleHighlander,
|
||||||
|
|
||||||
MaleWildwood = Api.Enums.ApiCollectionType.MaleWildwood,
|
MaleWildwood = Api.Enums.ApiCollectionType.MaleWildwood,
|
||||||
FemaleWildwood = Api.Enums.ApiCollectionType.FemaleWildwood,
|
FemaleWildwood = Api.Enums.ApiCollectionType.FemaleWildwood,
|
||||||
MaleDuskwight = Api.Enums.ApiCollectionType.MaleDuskwight,
|
MaleDuskwight = Api.Enums.ApiCollectionType.MaleDuskwight,
|
||||||
FemaleDuskwight = Api.Enums.ApiCollectionType.FemaleDuskwight,
|
FemaleDuskwight = Api.Enums.ApiCollectionType.FemaleDuskwight,
|
||||||
|
|
||||||
MalePlainsfolk = Api.Enums.ApiCollectionType.MalePlainsfolk,
|
MalePlainsfolk = Api.Enums.ApiCollectionType.MalePlainsfolk,
|
||||||
FemalePlainsfolk = Api.Enums.ApiCollectionType.FemalePlainsfolk,
|
FemalePlainsfolk = Api.Enums.ApiCollectionType.FemalePlainsfolk,
|
||||||
MaleDunesfolk = Api.Enums.ApiCollectionType.MaleDunesfolk,
|
MaleDunesfolk = Api.Enums.ApiCollectionType.MaleDunesfolk,
|
||||||
FemaleDunesfolk = Api.Enums.ApiCollectionType.FemaleDunesfolk,
|
FemaleDunesfolk = Api.Enums.ApiCollectionType.FemaleDunesfolk,
|
||||||
|
|
||||||
MaleSeekerOfTheSun = Api.Enums.ApiCollectionType.MaleSeekerOfTheSun,
|
MaleSeekerOfTheSun = Api.Enums.ApiCollectionType.MaleSeekerOfTheSun,
|
||||||
FemaleSeekerOfTheSun = Api.Enums.ApiCollectionType.FemaleSeekerOfTheSun,
|
FemaleSeekerOfTheSun = Api.Enums.ApiCollectionType.FemaleSeekerOfTheSun,
|
||||||
MaleKeeperOfTheMoon = Api.Enums.ApiCollectionType.MaleKeeperOfTheMoon,
|
MaleKeeperOfTheMoon = Api.Enums.ApiCollectionType.MaleKeeperOfTheMoon,
|
||||||
FemaleKeeperOfTheMoon = Api.Enums.ApiCollectionType.FemaleKeeperOfTheMoon,
|
FemaleKeeperOfTheMoon = Api.Enums.ApiCollectionType.FemaleKeeperOfTheMoon,
|
||||||
|
|
||||||
MaleSeawolf = Api.Enums.ApiCollectionType.MaleSeawolf,
|
MaleSeawolf = Api.Enums.ApiCollectionType.MaleSeawolf,
|
||||||
FemaleSeawolf = Api.Enums.ApiCollectionType.FemaleSeawolf,
|
FemaleSeawolf = Api.Enums.ApiCollectionType.FemaleSeawolf,
|
||||||
MaleHellsguard = Api.Enums.ApiCollectionType.MaleHellsguard,
|
MaleHellsguard = Api.Enums.ApiCollectionType.MaleHellsguard,
|
||||||
FemaleHellsguard = Api.Enums.ApiCollectionType.FemaleHellsguard,
|
FemaleHellsguard = Api.Enums.ApiCollectionType.FemaleHellsguard,
|
||||||
|
|
||||||
MaleRaen = Api.Enums.ApiCollectionType.MaleRaen,
|
MaleRaen = Api.Enums.ApiCollectionType.MaleRaen,
|
||||||
FemaleRaen = Api.Enums.ApiCollectionType.FemaleRaen,
|
FemaleRaen = Api.Enums.ApiCollectionType.FemaleRaen,
|
||||||
MaleXaela = Api.Enums.ApiCollectionType.MaleXaela,
|
MaleXaela = Api.Enums.ApiCollectionType.MaleXaela,
|
||||||
FemaleXaela = Api.Enums.ApiCollectionType.FemaleXaela,
|
FemaleXaela = Api.Enums.ApiCollectionType.FemaleXaela,
|
||||||
|
|
||||||
MaleHelion = Api.Enums.ApiCollectionType.MaleHelion,
|
MaleHelion = Api.Enums.ApiCollectionType.MaleHelion,
|
||||||
FemaleHelion = Api.Enums.ApiCollectionType.FemaleHelion,
|
FemaleHelion = Api.Enums.ApiCollectionType.FemaleHelion,
|
||||||
MaleLost = Api.Enums.ApiCollectionType.MaleLost,
|
MaleLost = Api.Enums.ApiCollectionType.MaleLost,
|
||||||
FemaleLost = Api.Enums.ApiCollectionType.FemaleLost,
|
FemaleLost = Api.Enums.ApiCollectionType.FemaleLost,
|
||||||
|
|
||||||
MaleRava = Api.Enums.ApiCollectionType.MaleRava,
|
MaleRava = Api.Enums.ApiCollectionType.MaleRava,
|
||||||
FemaleRava = Api.Enums.ApiCollectionType.FemaleRava,
|
FemaleRava = Api.Enums.ApiCollectionType.FemaleRava,
|
||||||
MaleVeena = Api.Enums.ApiCollectionType.MaleVeena,
|
MaleVeena = Api.Enums.ApiCollectionType.MaleVeena,
|
||||||
FemaleVeena = Api.Enums.ApiCollectionType.FemaleVeena,
|
FemaleVeena = Api.Enums.ApiCollectionType.FemaleVeena,
|
||||||
|
|
||||||
MaleMidlanderNpc = Api.Enums.ApiCollectionType.MaleMidlanderNpc,
|
MaleMidlanderNpc = Api.Enums.ApiCollectionType.MaleMidlanderNpc,
|
||||||
FemaleMidlanderNpc = Api.Enums.ApiCollectionType.FemaleMidlanderNpc,
|
FemaleMidlanderNpc = Api.Enums.ApiCollectionType.FemaleMidlanderNpc,
|
||||||
MaleHighlanderNpc = Api.Enums.ApiCollectionType.MaleHighlanderNpc,
|
MaleHighlanderNpc = Api.Enums.ApiCollectionType.MaleHighlanderNpc,
|
||||||
FemaleHighlanderNpc = Api.Enums.ApiCollectionType.FemaleHighlanderNpc,
|
FemaleHighlanderNpc = Api.Enums.ApiCollectionType.FemaleHighlanderNpc,
|
||||||
|
|
||||||
MaleWildwoodNpc = Api.Enums.ApiCollectionType.MaleWildwoodNpc,
|
MaleWildwoodNpc = Api.Enums.ApiCollectionType.MaleWildwoodNpc,
|
||||||
FemaleWildwoodNpc = Api.Enums.ApiCollectionType.FemaleWildwoodNpc,
|
FemaleWildwoodNpc = Api.Enums.ApiCollectionType.FemaleWildwoodNpc,
|
||||||
MaleDuskwightNpc = Api.Enums.ApiCollectionType.MaleDuskwightNpc,
|
MaleDuskwightNpc = Api.Enums.ApiCollectionType.MaleDuskwightNpc,
|
||||||
FemaleDuskwightNpc = Api.Enums.ApiCollectionType.FemaleDuskwightNpc,
|
FemaleDuskwightNpc = Api.Enums.ApiCollectionType.FemaleDuskwightNpc,
|
||||||
|
|
||||||
MalePlainsfolkNpc = Api.Enums.ApiCollectionType.MalePlainsfolkNpc,
|
MalePlainsfolkNpc = Api.Enums.ApiCollectionType.MalePlainsfolkNpc,
|
||||||
FemalePlainsfolkNpc = Api.Enums.ApiCollectionType.FemalePlainsfolkNpc,
|
FemalePlainsfolkNpc = Api.Enums.ApiCollectionType.FemalePlainsfolkNpc,
|
||||||
MaleDunesfolkNpc = Api.Enums.ApiCollectionType.MaleDunesfolkNpc,
|
MaleDunesfolkNpc = Api.Enums.ApiCollectionType.MaleDunesfolkNpc,
|
||||||
FemaleDunesfolkNpc = Api.Enums.ApiCollectionType.FemaleDunesfolkNpc,
|
FemaleDunesfolkNpc = Api.Enums.ApiCollectionType.FemaleDunesfolkNpc,
|
||||||
|
|
||||||
MaleSeekerOfTheSunNpc = Api.Enums.ApiCollectionType.MaleSeekerOfTheSunNpc,
|
MaleSeekerOfTheSunNpc = Api.Enums.ApiCollectionType.MaleSeekerOfTheSunNpc,
|
||||||
FemaleSeekerOfTheSunNpc = Api.Enums.ApiCollectionType.FemaleSeekerOfTheSunNpc,
|
FemaleSeekerOfTheSunNpc = Api.Enums.ApiCollectionType.FemaleSeekerOfTheSunNpc,
|
||||||
MaleKeeperOfTheMoonNpc = Api.Enums.ApiCollectionType.MaleKeeperOfTheMoonNpc,
|
MaleKeeperOfTheMoonNpc = Api.Enums.ApiCollectionType.MaleKeeperOfTheMoonNpc,
|
||||||
FemaleKeeperOfTheMoonNpc = Api.Enums.ApiCollectionType.FemaleKeeperOfTheMoonNpc,
|
FemaleKeeperOfTheMoonNpc = Api.Enums.ApiCollectionType.FemaleKeeperOfTheMoonNpc,
|
||||||
|
|
||||||
MaleSeawolfNpc = Api.Enums.ApiCollectionType.MaleSeawolfNpc,
|
MaleSeawolfNpc = Api.Enums.ApiCollectionType.MaleSeawolfNpc,
|
||||||
FemaleSeawolfNpc = Api.Enums.ApiCollectionType.FemaleSeawolfNpc,
|
FemaleSeawolfNpc = Api.Enums.ApiCollectionType.FemaleSeawolfNpc,
|
||||||
MaleHellsguardNpc = Api.Enums.ApiCollectionType.MaleHellsguardNpc,
|
MaleHellsguardNpc = Api.Enums.ApiCollectionType.MaleHellsguardNpc,
|
||||||
FemaleHellsguardNpc = Api.Enums.ApiCollectionType.FemaleHellsguardNpc,
|
FemaleHellsguardNpc = Api.Enums.ApiCollectionType.FemaleHellsguardNpc,
|
||||||
|
|
||||||
MaleRaenNpc = Api.Enums.ApiCollectionType.MaleRaenNpc,
|
MaleRaenNpc = Api.Enums.ApiCollectionType.MaleRaenNpc,
|
||||||
FemaleRaenNpc = Api.Enums.ApiCollectionType.FemaleRaenNpc,
|
FemaleRaenNpc = Api.Enums.ApiCollectionType.FemaleRaenNpc,
|
||||||
MaleXaelaNpc = Api.Enums.ApiCollectionType.MaleXaelaNpc,
|
MaleXaelaNpc = Api.Enums.ApiCollectionType.MaleXaelaNpc,
|
||||||
FemaleXaelaNpc = Api.Enums.ApiCollectionType.FemaleXaelaNpc,
|
FemaleXaelaNpc = Api.Enums.ApiCollectionType.FemaleXaelaNpc,
|
||||||
|
|
||||||
MaleHelionNpc = Api.Enums.ApiCollectionType.MaleHelionNpc,
|
MaleHelionNpc = Api.Enums.ApiCollectionType.MaleHelionNpc,
|
||||||
FemaleHelionNpc = Api.Enums.ApiCollectionType.FemaleHelionNpc,
|
FemaleHelionNpc = Api.Enums.ApiCollectionType.FemaleHelionNpc,
|
||||||
MaleLostNpc = Api.Enums.ApiCollectionType.MaleLostNpc,
|
MaleLostNpc = Api.Enums.ApiCollectionType.MaleLostNpc,
|
||||||
FemaleLostNpc = Api.Enums.ApiCollectionType.FemaleLostNpc,
|
FemaleLostNpc = Api.Enums.ApiCollectionType.FemaleLostNpc,
|
||||||
|
|
||||||
MaleRavaNpc = Api.Enums.ApiCollectionType.MaleRavaNpc,
|
MaleRavaNpc = Api.Enums.ApiCollectionType.MaleRavaNpc,
|
||||||
FemaleRavaNpc = Api.Enums.ApiCollectionType.FemaleRavaNpc,
|
FemaleRavaNpc = Api.Enums.ApiCollectionType.FemaleRavaNpc,
|
||||||
MaleVeenaNpc = Api.Enums.ApiCollectionType.MaleVeenaNpc,
|
MaleVeenaNpc = Api.Enums.ApiCollectionType.MaleVeenaNpc,
|
||||||
FemaleVeenaNpc = Api.Enums.ApiCollectionType.FemaleVeenaNpc,
|
FemaleVeenaNpc = Api.Enums.ApiCollectionType.FemaleVeenaNpc,
|
||||||
|
|
||||||
Default = Api.Enums.ApiCollectionType.Default, // The default collection was changed
|
Default = Api.Enums.ApiCollectionType.Default, // The default collection was changed
|
||||||
Interface = Api.Enums.ApiCollectionType.Interface, // The ui collection was changed
|
Interface = Api.Enums.ApiCollectionType.Interface, // The ui collection was changed
|
||||||
Current = Api.Enums.ApiCollectionType.Current, // The current collection was changed
|
Current = Api.Enums.ApiCollectionType.Current, // The current collection was changed
|
||||||
Individual, // An individual collection was changed
|
Individual, // An individual collection was changed
|
||||||
Inactive, // A collection was added or removed
|
Inactive, // A collection was added or removed
|
||||||
Temporary, // A temporary collections was set or deleted via IPC
|
Temporary, // A temporary collections was set or deleted via IPC
|
||||||
|
|
@ -111,202 +111,200 @@ public static class CollectionTypeExtensions
|
||||||
=> collectionType < CollectionType.Default;
|
=> collectionType < CollectionType.Default;
|
||||||
|
|
||||||
public static readonly (CollectionType, string, string)[] Special = Enum.GetValues<CollectionType>()
|
public static readonly (CollectionType, string, string)[] Special = Enum.GetValues<CollectionType>()
|
||||||
.Where(IsSpecial)
|
.Where(IsSpecial)
|
||||||
.Select(s => (s, s.ToName(), s.ToDescription()))
|
.Select(s => (s, s.ToName(), s.ToDescription()))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
public static CollectionType FromParts(Gender gender, bool npc)
|
public static CollectionType FromParts(Gender gender, bool npc)
|
||||||
{
|
{
|
||||||
gender = gender switch
|
gender = gender switch
|
||||||
{
|
{
|
||||||
Gender.MaleNpc => Gender.Male,
|
Gender.MaleNpc => Gender.Male,
|
||||||
Gender.FemaleNpc => Gender.Female,
|
Gender.FemaleNpc => Gender.Female,
|
||||||
_ => gender,
|
_ => gender,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (gender, npc) switch
|
return (gender, npc) switch
|
||||||
{
|
{
|
||||||
(Gender.Male, false) => CollectionType.MalePlayerCharacter,
|
(Gender.Male, false) => CollectionType.MalePlayerCharacter,
|
||||||
(Gender.Female, false) => CollectionType.FemalePlayerCharacter,
|
(Gender.Female, false) => CollectionType.FemalePlayerCharacter,
|
||||||
(Gender.Male, true) => CollectionType.MaleNonPlayerCharacter,
|
(Gender.Male, true) => CollectionType.MaleNonPlayerCharacter,
|
||||||
(Gender.Female, true) => CollectionType.FemaleNonPlayerCharacter,
|
(Gender.Female, true) => CollectionType.FemaleNonPlayerCharacter,
|
||||||
_ => CollectionType.Inactive,
|
_ => CollectionType.Inactive,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
private static readonly IReadOnlyList<CollectionType> DefaultList = new[] { CollectionType.Default };
|
private static readonly IReadOnlyList<CollectionType> DefaultList = new[] { CollectionType.Default };
|
||||||
private static readonly IReadOnlyList<CollectionType> MalePlayerList = new[] { CollectionType.MalePlayerCharacter, CollectionType.Default };
|
private static readonly IReadOnlyList<CollectionType> MalePlayerList = new[] { CollectionType.MalePlayerCharacter, CollectionType.Default };
|
||||||
private static readonly IReadOnlyList<CollectionType> FemalePlayerList = new[] { CollectionType.FemalePlayerCharacter, CollectionType.Default };
|
private static readonly IReadOnlyList<CollectionType> FemalePlayerList = new[] { CollectionType.FemalePlayerCharacter, CollectionType.Default };
|
||||||
private static readonly IReadOnlyList<CollectionType> MaleNpcList = new[] { CollectionType.MaleNonPlayerCharacter, CollectionType.Default };
|
private static readonly IReadOnlyList<CollectionType> MaleNpcList = new[] { CollectionType.MaleNonPlayerCharacter, CollectionType.Default };
|
||||||
private static readonly IReadOnlyList<CollectionType> FemaleNpcList = new[] { CollectionType.FemaleNonPlayerCharacter, CollectionType.Default };
|
private static readonly IReadOnlyList<CollectionType> FemaleNpcList = new[] { CollectionType.FemaleNonPlayerCharacter, CollectionType.Default };
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
/// <summary> A list of definite redundancy possibilities. </summary>
|
/// <summary> A list of definite redundancy possibilities. </summary>
|
||||||
public static IReadOnlyList<CollectionType> InheritanceOrder(this CollectionType collectionType)
|
public static IReadOnlyList<CollectionType> InheritanceOrder(this CollectionType collectionType)
|
||||||
=> collectionType switch
|
=> collectionType switch
|
||||||
{
|
{
|
||||||
CollectionType.Yourself => DefaultList,
|
CollectionType.Yourself => DefaultList,
|
||||||
CollectionType.MalePlayerCharacter => DefaultList,
|
CollectionType.MalePlayerCharacter => DefaultList,
|
||||||
CollectionType.FemalePlayerCharacter => DefaultList,
|
CollectionType.FemalePlayerCharacter => DefaultList,
|
||||||
CollectionType.MaleNonPlayerCharacter => DefaultList,
|
CollectionType.MaleNonPlayerCharacter => DefaultList,
|
||||||
CollectionType.FemaleNonPlayerCharacter => DefaultList,
|
CollectionType.FemaleNonPlayerCharacter => DefaultList,
|
||||||
CollectionType.MaleMidlander => MalePlayerList,
|
CollectionType.MaleMidlander => MalePlayerList,
|
||||||
CollectionType.FemaleMidlander => FemalePlayerList,
|
CollectionType.FemaleMidlander => FemalePlayerList,
|
||||||
CollectionType.MaleHighlander => MalePlayerList,
|
CollectionType.MaleHighlander => MalePlayerList,
|
||||||
CollectionType.FemaleHighlander => FemalePlayerList,
|
CollectionType.FemaleHighlander => FemalePlayerList,
|
||||||
CollectionType.MaleWildwood => MalePlayerList,
|
CollectionType.MaleWildwood => MalePlayerList,
|
||||||
CollectionType.FemaleWildwood => FemalePlayerList,
|
CollectionType.FemaleWildwood => FemalePlayerList,
|
||||||
CollectionType.MaleDuskwight => MalePlayerList,
|
CollectionType.MaleDuskwight => MalePlayerList,
|
||||||
CollectionType.FemaleDuskwight => FemalePlayerList,
|
CollectionType.FemaleDuskwight => FemalePlayerList,
|
||||||
CollectionType.MalePlainsfolk => MalePlayerList,
|
CollectionType.MalePlainsfolk => MalePlayerList,
|
||||||
CollectionType.FemalePlainsfolk => FemalePlayerList,
|
CollectionType.FemalePlainsfolk => FemalePlayerList,
|
||||||
CollectionType.MaleDunesfolk => MalePlayerList,
|
CollectionType.MaleDunesfolk => MalePlayerList,
|
||||||
CollectionType.FemaleDunesfolk => FemalePlayerList,
|
CollectionType.FemaleDunesfolk => FemalePlayerList,
|
||||||
CollectionType.MaleSeekerOfTheSun => MalePlayerList,
|
CollectionType.MaleSeekerOfTheSun => MalePlayerList,
|
||||||
CollectionType.FemaleSeekerOfTheSun => FemalePlayerList,
|
CollectionType.FemaleSeekerOfTheSun => FemalePlayerList,
|
||||||
CollectionType.MaleKeeperOfTheMoon => MalePlayerList,
|
CollectionType.MaleKeeperOfTheMoon => MalePlayerList,
|
||||||
CollectionType.FemaleKeeperOfTheMoon => FemalePlayerList,
|
CollectionType.FemaleKeeperOfTheMoon => FemalePlayerList,
|
||||||
CollectionType.MaleSeawolf => MalePlayerList,
|
CollectionType.MaleSeawolf => MalePlayerList,
|
||||||
CollectionType.FemaleSeawolf => FemalePlayerList,
|
CollectionType.FemaleSeawolf => FemalePlayerList,
|
||||||
CollectionType.MaleHellsguard => MalePlayerList,
|
CollectionType.MaleHellsguard => MalePlayerList,
|
||||||
CollectionType.FemaleHellsguard => FemalePlayerList,
|
CollectionType.FemaleHellsguard => FemalePlayerList,
|
||||||
CollectionType.MaleRaen => MalePlayerList,
|
CollectionType.MaleRaen => MalePlayerList,
|
||||||
CollectionType.FemaleRaen => FemalePlayerList,
|
CollectionType.FemaleRaen => FemalePlayerList,
|
||||||
CollectionType.MaleXaela => MalePlayerList,
|
CollectionType.MaleXaela => MalePlayerList,
|
||||||
CollectionType.FemaleXaela => FemalePlayerList,
|
CollectionType.FemaleXaela => FemalePlayerList,
|
||||||
CollectionType.MaleHelion => MalePlayerList,
|
CollectionType.MaleHelion => MalePlayerList,
|
||||||
CollectionType.FemaleHelion => FemalePlayerList,
|
CollectionType.FemaleHelion => FemalePlayerList,
|
||||||
CollectionType.MaleLost => MalePlayerList,
|
CollectionType.MaleLost => MalePlayerList,
|
||||||
CollectionType.FemaleLost => FemalePlayerList,
|
CollectionType.FemaleLost => FemalePlayerList,
|
||||||
CollectionType.MaleRava => MalePlayerList,
|
CollectionType.MaleRava => MalePlayerList,
|
||||||
CollectionType.FemaleRava => FemalePlayerList,
|
CollectionType.FemaleRava => FemalePlayerList,
|
||||||
CollectionType.MaleVeena => MalePlayerList,
|
CollectionType.MaleVeena => MalePlayerList,
|
||||||
CollectionType.FemaleVeena => FemalePlayerList,
|
CollectionType.FemaleVeena => FemalePlayerList,
|
||||||
CollectionType.MaleMidlanderNpc => MaleNpcList,
|
CollectionType.MaleMidlanderNpc => MaleNpcList,
|
||||||
CollectionType.FemaleMidlanderNpc => FemaleNpcList,
|
CollectionType.FemaleMidlanderNpc => FemaleNpcList,
|
||||||
CollectionType.MaleHighlanderNpc => MaleNpcList,
|
CollectionType.MaleHighlanderNpc => MaleNpcList,
|
||||||
CollectionType.FemaleHighlanderNpc => FemaleNpcList,
|
CollectionType.FemaleHighlanderNpc => FemaleNpcList,
|
||||||
CollectionType.MaleWildwoodNpc => MaleNpcList,
|
CollectionType.MaleWildwoodNpc => MaleNpcList,
|
||||||
CollectionType.FemaleWildwoodNpc => FemaleNpcList,
|
CollectionType.FemaleWildwoodNpc => FemaleNpcList,
|
||||||
CollectionType.MaleDuskwightNpc => MaleNpcList,
|
CollectionType.MaleDuskwightNpc => MaleNpcList,
|
||||||
CollectionType.FemaleDuskwightNpc => FemaleNpcList,
|
CollectionType.FemaleDuskwightNpc => FemaleNpcList,
|
||||||
CollectionType.MalePlainsfolkNpc => MaleNpcList,
|
CollectionType.MalePlainsfolkNpc => MaleNpcList,
|
||||||
CollectionType.FemalePlainsfolkNpc => FemaleNpcList,
|
CollectionType.FemalePlainsfolkNpc => FemaleNpcList,
|
||||||
CollectionType.MaleDunesfolkNpc => MaleNpcList,
|
CollectionType.MaleDunesfolkNpc => MaleNpcList,
|
||||||
CollectionType.FemaleDunesfolkNpc => FemaleNpcList,
|
CollectionType.FemaleDunesfolkNpc => FemaleNpcList,
|
||||||
CollectionType.MaleSeekerOfTheSunNpc => MaleNpcList,
|
CollectionType.MaleSeekerOfTheSunNpc => MaleNpcList,
|
||||||
CollectionType.FemaleSeekerOfTheSunNpc => FemaleNpcList,
|
CollectionType.FemaleSeekerOfTheSunNpc => FemaleNpcList,
|
||||||
CollectionType.MaleKeeperOfTheMoonNpc => MaleNpcList,
|
CollectionType.MaleKeeperOfTheMoonNpc => MaleNpcList,
|
||||||
CollectionType.FemaleKeeperOfTheMoonNpc => FemaleNpcList,
|
CollectionType.FemaleKeeperOfTheMoonNpc => FemaleNpcList,
|
||||||
CollectionType.MaleSeawolfNpc => MaleNpcList,
|
CollectionType.MaleSeawolfNpc => MaleNpcList,
|
||||||
CollectionType.FemaleSeawolfNpc => FemaleNpcList,
|
CollectionType.FemaleSeawolfNpc => FemaleNpcList,
|
||||||
CollectionType.MaleHellsguardNpc => MaleNpcList,
|
CollectionType.MaleHellsguardNpc => MaleNpcList,
|
||||||
CollectionType.FemaleHellsguardNpc => FemaleNpcList,
|
CollectionType.FemaleHellsguardNpc => FemaleNpcList,
|
||||||
CollectionType.MaleRaenNpc => MaleNpcList,
|
CollectionType.MaleRaenNpc => MaleNpcList,
|
||||||
CollectionType.FemaleRaenNpc => FemaleNpcList,
|
CollectionType.FemaleRaenNpc => FemaleNpcList,
|
||||||
CollectionType.MaleXaelaNpc => MaleNpcList,
|
CollectionType.MaleXaelaNpc => MaleNpcList,
|
||||||
CollectionType.FemaleXaelaNpc => FemaleNpcList,
|
CollectionType.FemaleXaelaNpc => FemaleNpcList,
|
||||||
CollectionType.MaleHelionNpc => MaleNpcList,
|
CollectionType.MaleHelionNpc => MaleNpcList,
|
||||||
CollectionType.FemaleHelionNpc => FemaleNpcList,
|
CollectionType.FemaleHelionNpc => FemaleNpcList,
|
||||||
CollectionType.MaleLostNpc => MaleNpcList,
|
CollectionType.MaleLostNpc => MaleNpcList,
|
||||||
CollectionType.FemaleLostNpc => FemaleNpcList,
|
CollectionType.FemaleLostNpc => FemaleNpcList,
|
||||||
CollectionType.MaleRavaNpc => MaleNpcList,
|
CollectionType.MaleRavaNpc => MaleNpcList,
|
||||||
CollectionType.FemaleRavaNpc => FemaleNpcList,
|
CollectionType.FemaleRavaNpc => FemaleNpcList,
|
||||||
CollectionType.MaleVeenaNpc => MaleNpcList,
|
CollectionType.MaleVeenaNpc => MaleNpcList,
|
||||||
CollectionType.FemaleVeenaNpc => FemaleNpcList,
|
CollectionType.FemaleVeenaNpc => FemaleNpcList,
|
||||||
CollectionType.Individual => DefaultList,
|
CollectionType.Individual => DefaultList,
|
||||||
_ => Array.Empty<CollectionType>(),
|
_ => Array.Empty<CollectionType>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static CollectionType FromParts(SubRace race, Gender gender, bool npc)
|
public static CollectionType FromParts(SubRace race, Gender gender, bool npc)
|
||||||
{
|
{
|
||||||
gender = gender switch
|
gender = gender switch
|
||||||
{
|
{
|
||||||
Gender.MaleNpc => Gender.Male,
|
Gender.MaleNpc => Gender.Male,
|
||||||
Gender.FemaleNpc => Gender.Female,
|
Gender.FemaleNpc => Gender.Female,
|
||||||
_ => gender,
|
_ => gender,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (race, gender, npc) switch
|
return (race, gender, npc) switch
|
||||||
{
|
{
|
||||||
(SubRace.Midlander, Gender.Male, false) => CollectionType.MaleMidlander,
|
(SubRace.Midlander, Gender.Male, false) => CollectionType.MaleMidlander,
|
||||||
(SubRace.Highlander, Gender.Male, false) => CollectionType.MaleHighlander,
|
(SubRace.Highlander, Gender.Male, false) => CollectionType.MaleHighlander,
|
||||||
(SubRace.Wildwood, Gender.Male, false) => CollectionType.MaleWildwood,
|
(SubRace.Wildwood, Gender.Male, false) => CollectionType.MaleWildwood,
|
||||||
(SubRace.Duskwight, Gender.Male, false) => CollectionType.MaleDuskwight,
|
(SubRace.Duskwight, Gender.Male, false) => CollectionType.MaleDuskwight,
|
||||||
(SubRace.Plainsfolk, Gender.Male, false) => CollectionType.MalePlainsfolk,
|
(SubRace.Plainsfolk, Gender.Male, false) => CollectionType.MalePlainsfolk,
|
||||||
(SubRace.Dunesfolk, Gender.Male, false) => CollectionType.MaleDunesfolk,
|
(SubRace.Dunesfolk, Gender.Male, false) => CollectionType.MaleDunesfolk,
|
||||||
(SubRace.SeekerOfTheSun, Gender.Male, false) => CollectionType.MaleSeekerOfTheSun,
|
(SubRace.SeekerOfTheSun, Gender.Male, false) => CollectionType.MaleSeekerOfTheSun,
|
||||||
(SubRace.KeeperOfTheMoon, Gender.Male, false) => CollectionType.MaleKeeperOfTheMoon,
|
(SubRace.KeeperOfTheMoon, Gender.Male, false) => CollectionType.MaleKeeperOfTheMoon,
|
||||||
(SubRace.Seawolf, Gender.Male, false) => CollectionType.MaleSeawolf,
|
(SubRace.Seawolf, Gender.Male, false) => CollectionType.MaleSeawolf,
|
||||||
(SubRace.Hellsguard, Gender.Male, false) => CollectionType.MaleHellsguard,
|
(SubRace.Hellsguard, Gender.Male, false) => CollectionType.MaleHellsguard,
|
||||||
(SubRace.Raen, Gender.Male, false) => CollectionType.MaleRaen,
|
(SubRace.Raen, Gender.Male, false) => CollectionType.MaleRaen,
|
||||||
(SubRace.Xaela, Gender.Male, false) => CollectionType.MaleXaela,
|
(SubRace.Xaela, Gender.Male, false) => CollectionType.MaleXaela,
|
||||||
(SubRace.Helion, Gender.Male, false) => CollectionType.MaleHelion,
|
(SubRace.Helion, Gender.Male, false) => CollectionType.MaleHelion,
|
||||||
(SubRace.Lost, Gender.Male, false) => CollectionType.MaleLost,
|
(SubRace.Lost, Gender.Male, false) => CollectionType.MaleLost,
|
||||||
(SubRace.Rava, Gender.Male, false) => CollectionType.MaleRava,
|
(SubRace.Rava, Gender.Male, false) => CollectionType.MaleRava,
|
||||||
(SubRace.Veena, Gender.Male, false) => CollectionType.MaleVeena,
|
(SubRace.Veena, Gender.Male, false) => CollectionType.MaleVeena,
|
||||||
|
|
||||||
(SubRace.Midlander, Gender.Female, false) => CollectionType.FemaleMidlander,
|
(SubRace.Midlander, Gender.Female, false) => CollectionType.FemaleMidlander,
|
||||||
(SubRace.Highlander, Gender.Female, false) => CollectionType.FemaleHighlander,
|
(SubRace.Highlander, Gender.Female, false) => CollectionType.FemaleHighlander,
|
||||||
(SubRace.Wildwood, Gender.Female, false) => CollectionType.FemaleWildwood,
|
(SubRace.Wildwood, Gender.Female, false) => CollectionType.FemaleWildwood,
|
||||||
(SubRace.Duskwight, Gender.Female, false) => CollectionType.FemaleDuskwight,
|
(SubRace.Duskwight, Gender.Female, false) => CollectionType.FemaleDuskwight,
|
||||||
(SubRace.Plainsfolk, Gender.Female, false) => CollectionType.FemalePlainsfolk,
|
(SubRace.Plainsfolk, Gender.Female, false) => CollectionType.FemalePlainsfolk,
|
||||||
(SubRace.Dunesfolk, Gender.Female, false) => CollectionType.FemaleDunesfolk,
|
(SubRace.Dunesfolk, Gender.Female, false) => CollectionType.FemaleDunesfolk,
|
||||||
(SubRace.SeekerOfTheSun, Gender.Female, false) => CollectionType.FemaleSeekerOfTheSun,
|
(SubRace.SeekerOfTheSun, Gender.Female, false) => CollectionType.FemaleSeekerOfTheSun,
|
||||||
(SubRace.KeeperOfTheMoon, Gender.Female, false) => CollectionType.FemaleKeeperOfTheMoon,
|
(SubRace.KeeperOfTheMoon, Gender.Female, false) => CollectionType.FemaleKeeperOfTheMoon,
|
||||||
(SubRace.Seawolf, Gender.Female, false) => CollectionType.FemaleSeawolf,
|
(SubRace.Seawolf, Gender.Female, false) => CollectionType.FemaleSeawolf,
|
||||||
(SubRace.Hellsguard, Gender.Female, false) => CollectionType.FemaleHellsguard,
|
(SubRace.Hellsguard, Gender.Female, false) => CollectionType.FemaleHellsguard,
|
||||||
(SubRace.Raen, Gender.Female, false) => CollectionType.FemaleRaen,
|
(SubRace.Raen, Gender.Female, false) => CollectionType.FemaleRaen,
|
||||||
(SubRace.Xaela, Gender.Female, false) => CollectionType.FemaleXaela,
|
(SubRace.Xaela, Gender.Female, false) => CollectionType.FemaleXaela,
|
||||||
(SubRace.Helion, Gender.Female, false) => CollectionType.FemaleHelion,
|
(SubRace.Helion, Gender.Female, false) => CollectionType.FemaleHelion,
|
||||||
(SubRace.Lost, Gender.Female, false) => CollectionType.FemaleLost,
|
(SubRace.Lost, Gender.Female, false) => CollectionType.FemaleLost,
|
||||||
(SubRace.Rava, Gender.Female, false) => CollectionType.FemaleRava,
|
(SubRace.Rava, Gender.Female, false) => CollectionType.FemaleRava,
|
||||||
(SubRace.Veena, Gender.Female, false) => CollectionType.FemaleVeena,
|
(SubRace.Veena, Gender.Female, false) => CollectionType.FemaleVeena,
|
||||||
|
|
||||||
(SubRace.Midlander, Gender.Male, true) => CollectionType.MaleMidlanderNpc,
|
(SubRace.Midlander, Gender.Male, true) => CollectionType.MaleMidlanderNpc,
|
||||||
(SubRace.Highlander, Gender.Male, true) => CollectionType.MaleHighlanderNpc,
|
(SubRace.Highlander, Gender.Male, true) => CollectionType.MaleHighlanderNpc,
|
||||||
(SubRace.Wildwood, Gender.Male, true) => CollectionType.MaleWildwoodNpc,
|
(SubRace.Wildwood, Gender.Male, true) => CollectionType.MaleWildwoodNpc,
|
||||||
(SubRace.Duskwight, Gender.Male, true) => CollectionType.MaleDuskwightNpc,
|
(SubRace.Duskwight, Gender.Male, true) => CollectionType.MaleDuskwightNpc,
|
||||||
(SubRace.Plainsfolk, Gender.Male, true) => CollectionType.MalePlainsfolkNpc,
|
(SubRace.Plainsfolk, Gender.Male, true) => CollectionType.MalePlainsfolkNpc,
|
||||||
(SubRace.Dunesfolk, Gender.Male, true) => CollectionType.MaleDunesfolkNpc,
|
(SubRace.Dunesfolk, Gender.Male, true) => CollectionType.MaleDunesfolkNpc,
|
||||||
(SubRace.SeekerOfTheSun, Gender.Male, true) => CollectionType.MaleSeekerOfTheSunNpc,
|
(SubRace.SeekerOfTheSun, Gender.Male, true) => CollectionType.MaleSeekerOfTheSunNpc,
|
||||||
(SubRace.KeeperOfTheMoon, Gender.Male, true) => CollectionType.MaleKeeperOfTheMoonNpc,
|
(SubRace.KeeperOfTheMoon, Gender.Male, true) => CollectionType.MaleKeeperOfTheMoonNpc,
|
||||||
(SubRace.Seawolf, Gender.Male, true) => CollectionType.MaleSeawolfNpc,
|
(SubRace.Seawolf, Gender.Male, true) => CollectionType.MaleSeawolfNpc,
|
||||||
(SubRace.Hellsguard, Gender.Male, true) => CollectionType.MaleHellsguardNpc,
|
(SubRace.Hellsguard, Gender.Male, true) => CollectionType.MaleHellsguardNpc,
|
||||||
(SubRace.Raen, Gender.Male, true) => CollectionType.MaleRaenNpc,
|
(SubRace.Raen, Gender.Male, true) => CollectionType.MaleRaenNpc,
|
||||||
(SubRace.Xaela, Gender.Male, true) => CollectionType.MaleXaelaNpc,
|
(SubRace.Xaela, Gender.Male, true) => CollectionType.MaleXaelaNpc,
|
||||||
(SubRace.Helion, Gender.Male, true) => CollectionType.MaleHelionNpc,
|
(SubRace.Helion, Gender.Male, true) => CollectionType.MaleHelionNpc,
|
||||||
(SubRace.Lost, Gender.Male, true) => CollectionType.MaleLostNpc,
|
(SubRace.Lost, Gender.Male, true) => CollectionType.MaleLostNpc,
|
||||||
(SubRace.Rava, Gender.Male, true) => CollectionType.MaleRavaNpc,
|
(SubRace.Rava, Gender.Male, true) => CollectionType.MaleRavaNpc,
|
||||||
(SubRace.Veena, Gender.Male, true) => CollectionType.MaleVeenaNpc,
|
(SubRace.Veena, Gender.Male, true) => CollectionType.MaleVeenaNpc,
|
||||||
|
|
||||||
(SubRace.Midlander, Gender.Female, true) => CollectionType.FemaleMidlanderNpc,
|
(SubRace.Midlander, Gender.Female, true) => CollectionType.FemaleMidlanderNpc,
|
||||||
(SubRace.Highlander, Gender.Female, true) => CollectionType.FemaleHighlanderNpc,
|
(SubRace.Highlander, Gender.Female, true) => CollectionType.FemaleHighlanderNpc,
|
||||||
(SubRace.Wildwood, Gender.Female, true) => CollectionType.FemaleWildwoodNpc,
|
(SubRace.Wildwood, Gender.Female, true) => CollectionType.FemaleWildwoodNpc,
|
||||||
(SubRace.Duskwight, Gender.Female, true) => CollectionType.FemaleDuskwightNpc,
|
(SubRace.Duskwight, Gender.Female, true) => CollectionType.FemaleDuskwightNpc,
|
||||||
(SubRace.Plainsfolk, Gender.Female, true) => CollectionType.FemalePlainsfolkNpc,
|
(SubRace.Plainsfolk, Gender.Female, true) => CollectionType.FemalePlainsfolkNpc,
|
||||||
(SubRace.Dunesfolk, Gender.Female, true) => CollectionType.FemaleDunesfolkNpc,
|
(SubRace.Dunesfolk, Gender.Female, true) => CollectionType.FemaleDunesfolkNpc,
|
||||||
(SubRace.SeekerOfTheSun, Gender.Female, true) => CollectionType.FemaleSeekerOfTheSunNpc,
|
(SubRace.SeekerOfTheSun, Gender.Female, true) => CollectionType.FemaleSeekerOfTheSunNpc,
|
||||||
(SubRace.KeeperOfTheMoon, Gender.Female, true) => CollectionType.FemaleKeeperOfTheMoonNpc,
|
(SubRace.KeeperOfTheMoon, Gender.Female, true) => CollectionType.FemaleKeeperOfTheMoonNpc,
|
||||||
(SubRace.Seawolf, Gender.Female, true) => CollectionType.FemaleSeawolfNpc,
|
(SubRace.Seawolf, Gender.Female, true) => CollectionType.FemaleSeawolfNpc,
|
||||||
(SubRace.Hellsguard, Gender.Female, true) => CollectionType.FemaleHellsguardNpc,
|
(SubRace.Hellsguard, Gender.Female, true) => CollectionType.FemaleHellsguardNpc,
|
||||||
(SubRace.Raen, Gender.Female, true) => CollectionType.FemaleRaenNpc,
|
(SubRace.Raen, Gender.Female, true) => CollectionType.FemaleRaenNpc,
|
||||||
(SubRace.Xaela, Gender.Female, true) => CollectionType.FemaleXaelaNpc,
|
(SubRace.Xaela, Gender.Female, true) => CollectionType.FemaleXaelaNpc,
|
||||||
(SubRace.Helion, Gender.Female, true) => CollectionType.FemaleHelionNpc,
|
(SubRace.Helion, Gender.Female, true) => CollectionType.FemaleHelionNpc,
|
||||||
(SubRace.Lost, Gender.Female, true) => CollectionType.FemaleLostNpc,
|
(SubRace.Lost, Gender.Female, true) => CollectionType.FemaleLostNpc,
|
||||||
(SubRace.Rava, Gender.Female, true) => CollectionType.FemaleRavaNpc,
|
(SubRace.Rava, Gender.Female, true) => CollectionType.FemaleRavaNpc,
|
||||||
(SubRace.Veena, Gender.Female, true) => CollectionType.FemaleVeenaNpc,
|
(SubRace.Veena, Gender.Female, true) => CollectionType.FemaleVeenaNpc,
|
||||||
_ => CollectionType.Inactive,
|
_ => CollectionType.Inactive,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryParse(string text, out CollectionType type)
|
public static bool TryParse(string text, out CollectionType type)
|
||||||
{
|
{
|
||||||
if (Enum.TryParse(text, true, out type))
|
if (Enum.TryParse(text, true, out type))
|
||||||
{
|
|
||||||
return type is not CollectionType.Inactive and not CollectionType.Temporary;
|
return type is not CollectionType.Inactive and not CollectionType.Temporary;
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Equals(text, "character", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(text, "character", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|
@ -335,9 +333,7 @@ public static class CollectionTypeExtensions
|
||||||
foreach (var t in Enum.GetValues<CollectionType>())
|
foreach (var t in Enum.GetValues<CollectionType>())
|
||||||
{
|
{
|
||||||
if (t is CollectionType.Inactive or CollectionType.Temporary)
|
if (t is CollectionType.Inactive or CollectionType.Temporary)
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Equals(text, t.ToName(), StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(text, t.ToName(), StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|
@ -352,83 +348,83 @@ public static class CollectionTypeExtensions
|
||||||
public static string ToName(this CollectionType collectionType)
|
public static string ToName(this CollectionType collectionType)
|
||||||
=> collectionType switch
|
=> collectionType switch
|
||||||
{
|
{
|
||||||
CollectionType.Yourself => "Your Character",
|
CollectionType.Yourself => "Your Character",
|
||||||
CollectionType.NonPlayerChild => "Non-Player Children",
|
CollectionType.NonPlayerChild => "Non-Player Children",
|
||||||
CollectionType.NonPlayerElderly => "Non-Player Elderly",
|
CollectionType.NonPlayerElderly => "Non-Player Elderly",
|
||||||
CollectionType.MalePlayerCharacter => "Male Player Characters",
|
CollectionType.MalePlayerCharacter => "Male Player Characters",
|
||||||
CollectionType.MaleNonPlayerCharacter => "Male Non-Player Characters",
|
CollectionType.MaleNonPlayerCharacter => "Male Non-Player Characters",
|
||||||
CollectionType.MaleMidlander => $"Male {SubRace.Midlander.ToName()}",
|
CollectionType.MaleMidlander => $"Male {SubRace.Midlander.ToName()}",
|
||||||
CollectionType.MaleHighlander => $"Male {SubRace.Highlander.ToName()}",
|
CollectionType.MaleHighlander => $"Male {SubRace.Highlander.ToName()}",
|
||||||
CollectionType.MaleWildwood => $"Male {SubRace.Wildwood.ToName()}",
|
CollectionType.MaleWildwood => $"Male {SubRace.Wildwood.ToName()}",
|
||||||
CollectionType.MaleDuskwight => $"Male {SubRace.Duskwight.ToName()}",
|
CollectionType.MaleDuskwight => $"Male {SubRace.Duskwight.ToName()}",
|
||||||
CollectionType.MalePlainsfolk => $"Male {SubRace.Plainsfolk.ToName()}",
|
CollectionType.MalePlainsfolk => $"Male {SubRace.Plainsfolk.ToName()}",
|
||||||
CollectionType.MaleDunesfolk => $"Male {SubRace.Dunesfolk.ToName()}",
|
CollectionType.MaleDunesfolk => $"Male {SubRace.Dunesfolk.ToName()}",
|
||||||
CollectionType.MaleSeekerOfTheSun => $"Male {SubRace.SeekerOfTheSun.ToName()}",
|
CollectionType.MaleSeekerOfTheSun => $"Male {SubRace.SeekerOfTheSun.ToName()}",
|
||||||
CollectionType.MaleKeeperOfTheMoon => $"Male {SubRace.KeeperOfTheMoon.ToName()}",
|
CollectionType.MaleKeeperOfTheMoon => $"Male {SubRace.KeeperOfTheMoon.ToName()}",
|
||||||
CollectionType.MaleSeawolf => $"Male {SubRace.Seawolf.ToName()}",
|
CollectionType.MaleSeawolf => $"Male {SubRace.Seawolf.ToName()}",
|
||||||
CollectionType.MaleHellsguard => $"Male {SubRace.Hellsguard.ToName()}",
|
CollectionType.MaleHellsguard => $"Male {SubRace.Hellsguard.ToName()}",
|
||||||
CollectionType.MaleRaen => $"Male {SubRace.Raen.ToName()}",
|
CollectionType.MaleRaen => $"Male {SubRace.Raen.ToName()}",
|
||||||
CollectionType.MaleXaela => $"Male {SubRace.Xaela.ToName()}",
|
CollectionType.MaleXaela => $"Male {SubRace.Xaela.ToName()}",
|
||||||
CollectionType.MaleHelion => $"Male {SubRace.Helion.ToName()}",
|
CollectionType.MaleHelion => $"Male {SubRace.Helion.ToName()}",
|
||||||
CollectionType.MaleLost => $"Male {SubRace.Lost.ToName()}",
|
CollectionType.MaleLost => $"Male {SubRace.Lost.ToName()}",
|
||||||
CollectionType.MaleRava => $"Male {SubRace.Rava.ToName()}",
|
CollectionType.MaleRava => $"Male {SubRace.Rava.ToName()}",
|
||||||
CollectionType.MaleVeena => $"Male {SubRace.Veena.ToName()}",
|
CollectionType.MaleVeena => $"Male {SubRace.Veena.ToName()}",
|
||||||
CollectionType.MaleMidlanderNpc => $"Male {SubRace.Midlander.ToName()} (NPC)",
|
CollectionType.MaleMidlanderNpc => $"Male {SubRace.Midlander.ToName()} (NPC)",
|
||||||
CollectionType.MaleHighlanderNpc => $"Male {SubRace.Highlander.ToName()} (NPC)",
|
CollectionType.MaleHighlanderNpc => $"Male {SubRace.Highlander.ToName()} (NPC)",
|
||||||
CollectionType.MaleWildwoodNpc => $"Male {SubRace.Wildwood.ToName()} (NPC)",
|
CollectionType.MaleWildwoodNpc => $"Male {SubRace.Wildwood.ToName()} (NPC)",
|
||||||
CollectionType.MaleDuskwightNpc => $"Male {SubRace.Duskwight.ToName()} (NPC)",
|
CollectionType.MaleDuskwightNpc => $"Male {SubRace.Duskwight.ToName()} (NPC)",
|
||||||
CollectionType.MalePlainsfolkNpc => $"Male {SubRace.Plainsfolk.ToName()} (NPC)",
|
CollectionType.MalePlainsfolkNpc => $"Male {SubRace.Plainsfolk.ToName()} (NPC)",
|
||||||
CollectionType.MaleDunesfolkNpc => $"Male {SubRace.Dunesfolk.ToName()} (NPC)",
|
CollectionType.MaleDunesfolkNpc => $"Male {SubRace.Dunesfolk.ToName()} (NPC)",
|
||||||
CollectionType.MaleSeekerOfTheSunNpc => $"Male {SubRace.SeekerOfTheSun.ToName()} (NPC)",
|
CollectionType.MaleSeekerOfTheSunNpc => $"Male {SubRace.SeekerOfTheSun.ToName()} (NPC)",
|
||||||
CollectionType.MaleKeeperOfTheMoonNpc => $"Male {SubRace.KeeperOfTheMoon.ToName()} (NPC)",
|
CollectionType.MaleKeeperOfTheMoonNpc => $"Male {SubRace.KeeperOfTheMoon.ToName()} (NPC)",
|
||||||
CollectionType.MaleSeawolfNpc => $"Male {SubRace.Seawolf.ToName()} (NPC)",
|
CollectionType.MaleSeawolfNpc => $"Male {SubRace.Seawolf.ToName()} (NPC)",
|
||||||
CollectionType.MaleHellsguardNpc => $"Male {SubRace.Hellsguard.ToName()} (NPC)",
|
CollectionType.MaleHellsguardNpc => $"Male {SubRace.Hellsguard.ToName()} (NPC)",
|
||||||
CollectionType.MaleRaenNpc => $"Male {SubRace.Raen.ToName()} (NPC)",
|
CollectionType.MaleRaenNpc => $"Male {SubRace.Raen.ToName()} (NPC)",
|
||||||
CollectionType.MaleXaelaNpc => $"Male {SubRace.Xaela.ToName()} (NPC)",
|
CollectionType.MaleXaelaNpc => $"Male {SubRace.Xaela.ToName()} (NPC)",
|
||||||
CollectionType.MaleHelionNpc => $"Male {SubRace.Helion.ToName()} (NPC)",
|
CollectionType.MaleHelionNpc => $"Male {SubRace.Helion.ToName()} (NPC)",
|
||||||
CollectionType.MaleLostNpc => $"Male {SubRace.Lost.ToName()} (NPC)",
|
CollectionType.MaleLostNpc => $"Male {SubRace.Lost.ToName()} (NPC)",
|
||||||
CollectionType.MaleRavaNpc => $"Male {SubRace.Rava.ToName()} (NPC)",
|
CollectionType.MaleRavaNpc => $"Male {SubRace.Rava.ToName()} (NPC)",
|
||||||
CollectionType.MaleVeenaNpc => $"Male {SubRace.Veena.ToName()} (NPC)",
|
CollectionType.MaleVeenaNpc => $"Male {SubRace.Veena.ToName()} (NPC)",
|
||||||
CollectionType.FemalePlayerCharacter => "Female Player Characters",
|
CollectionType.FemalePlayerCharacter => "Female Player Characters",
|
||||||
CollectionType.FemaleNonPlayerCharacter => "Female Non-Player Characters",
|
CollectionType.FemaleNonPlayerCharacter => "Female Non-Player Characters",
|
||||||
CollectionType.FemaleMidlander => $"Female {SubRace.Midlander.ToName()}",
|
CollectionType.FemaleMidlander => $"Female {SubRace.Midlander.ToName()}",
|
||||||
CollectionType.FemaleHighlander => $"Female {SubRace.Highlander.ToName()}",
|
CollectionType.FemaleHighlander => $"Female {SubRace.Highlander.ToName()}",
|
||||||
CollectionType.FemaleWildwood => $"Female {SubRace.Wildwood.ToName()}",
|
CollectionType.FemaleWildwood => $"Female {SubRace.Wildwood.ToName()}",
|
||||||
CollectionType.FemaleDuskwight => $"Female {SubRace.Duskwight.ToName()}",
|
CollectionType.FemaleDuskwight => $"Female {SubRace.Duskwight.ToName()}",
|
||||||
CollectionType.FemalePlainsfolk => $"Female {SubRace.Plainsfolk.ToName()}",
|
CollectionType.FemalePlainsfolk => $"Female {SubRace.Plainsfolk.ToName()}",
|
||||||
CollectionType.FemaleDunesfolk => $"Female {SubRace.Dunesfolk.ToName()}",
|
CollectionType.FemaleDunesfolk => $"Female {SubRace.Dunesfolk.ToName()}",
|
||||||
CollectionType.FemaleSeekerOfTheSun => $"Female {SubRace.SeekerOfTheSun.ToName()}",
|
CollectionType.FemaleSeekerOfTheSun => $"Female {SubRace.SeekerOfTheSun.ToName()}",
|
||||||
CollectionType.FemaleKeeperOfTheMoon => $"Female {SubRace.KeeperOfTheMoon.ToName()}",
|
CollectionType.FemaleKeeperOfTheMoon => $"Female {SubRace.KeeperOfTheMoon.ToName()}",
|
||||||
CollectionType.FemaleSeawolf => $"Female {SubRace.Seawolf.ToName()}",
|
CollectionType.FemaleSeawolf => $"Female {SubRace.Seawolf.ToName()}",
|
||||||
CollectionType.FemaleHellsguard => $"Female {SubRace.Hellsguard.ToName()}",
|
CollectionType.FemaleHellsguard => $"Female {SubRace.Hellsguard.ToName()}",
|
||||||
CollectionType.FemaleRaen => $"Female {SubRace.Raen.ToName()}",
|
CollectionType.FemaleRaen => $"Female {SubRace.Raen.ToName()}",
|
||||||
CollectionType.FemaleXaela => $"Female {SubRace.Xaela.ToName()}",
|
CollectionType.FemaleXaela => $"Female {SubRace.Xaela.ToName()}",
|
||||||
CollectionType.FemaleHelion => $"Female {SubRace.Helion.ToName()}",
|
CollectionType.FemaleHelion => $"Female {SubRace.Helion.ToName()}",
|
||||||
CollectionType.FemaleLost => $"Female {SubRace.Lost.ToName()}",
|
CollectionType.FemaleLost => $"Female {SubRace.Lost.ToName()}",
|
||||||
CollectionType.FemaleRava => $"Female {SubRace.Rava.ToName()}",
|
CollectionType.FemaleRava => $"Female {SubRace.Rava.ToName()}",
|
||||||
CollectionType.FemaleVeena => $"Female {SubRace.Veena.ToName()}",
|
CollectionType.FemaleVeena => $"Female {SubRace.Veena.ToName()}",
|
||||||
CollectionType.FemaleMidlanderNpc => $"Female {SubRace.Midlander.ToName()} (NPC)",
|
CollectionType.FemaleMidlanderNpc => $"Female {SubRace.Midlander.ToName()} (NPC)",
|
||||||
CollectionType.FemaleHighlanderNpc => $"Female {SubRace.Highlander.ToName()} (NPC)",
|
CollectionType.FemaleHighlanderNpc => $"Female {SubRace.Highlander.ToName()} (NPC)",
|
||||||
CollectionType.FemaleWildwoodNpc => $"Female {SubRace.Wildwood.ToName()} (NPC)",
|
CollectionType.FemaleWildwoodNpc => $"Female {SubRace.Wildwood.ToName()} (NPC)",
|
||||||
CollectionType.FemaleDuskwightNpc => $"Female {SubRace.Duskwight.ToName()} (NPC)",
|
CollectionType.FemaleDuskwightNpc => $"Female {SubRace.Duskwight.ToName()} (NPC)",
|
||||||
CollectionType.FemalePlainsfolkNpc => $"Female {SubRace.Plainsfolk.ToName()} (NPC)",
|
CollectionType.FemalePlainsfolkNpc => $"Female {SubRace.Plainsfolk.ToName()} (NPC)",
|
||||||
CollectionType.FemaleDunesfolkNpc => $"Female {SubRace.Dunesfolk.ToName()} (NPC)",
|
CollectionType.FemaleDunesfolkNpc => $"Female {SubRace.Dunesfolk.ToName()} (NPC)",
|
||||||
CollectionType.FemaleSeekerOfTheSunNpc => $"Female {SubRace.SeekerOfTheSun.ToName()} (NPC)",
|
CollectionType.FemaleSeekerOfTheSunNpc => $"Female {SubRace.SeekerOfTheSun.ToName()} (NPC)",
|
||||||
CollectionType.FemaleKeeperOfTheMoonNpc => $"Female {SubRace.KeeperOfTheMoon.ToName()} (NPC)",
|
CollectionType.FemaleKeeperOfTheMoonNpc => $"Female {SubRace.KeeperOfTheMoon.ToName()} (NPC)",
|
||||||
CollectionType.FemaleSeawolfNpc => $"Female {SubRace.Seawolf.ToName()} (NPC)",
|
CollectionType.FemaleSeawolfNpc => $"Female {SubRace.Seawolf.ToName()} (NPC)",
|
||||||
CollectionType.FemaleHellsguardNpc => $"Female {SubRace.Hellsguard.ToName()} (NPC)",
|
CollectionType.FemaleHellsguardNpc => $"Female {SubRace.Hellsguard.ToName()} (NPC)",
|
||||||
CollectionType.FemaleRaenNpc => $"Female {SubRace.Raen.ToName()} (NPC)",
|
CollectionType.FemaleRaenNpc => $"Female {SubRace.Raen.ToName()} (NPC)",
|
||||||
CollectionType.FemaleXaelaNpc => $"Female {SubRace.Xaela.ToName()} (NPC)",
|
CollectionType.FemaleXaelaNpc => $"Female {SubRace.Xaela.ToName()} (NPC)",
|
||||||
CollectionType.FemaleHelionNpc => $"Female {SubRace.Helion.ToName()} (NPC)",
|
CollectionType.FemaleHelionNpc => $"Female {SubRace.Helion.ToName()} (NPC)",
|
||||||
CollectionType.FemaleLostNpc => $"Female {SubRace.Lost.ToName()} (NPC)",
|
CollectionType.FemaleLostNpc => $"Female {SubRace.Lost.ToName()} (NPC)",
|
||||||
CollectionType.FemaleRavaNpc => $"Female {SubRace.Rava.ToName()} (NPC)",
|
CollectionType.FemaleRavaNpc => $"Female {SubRace.Rava.ToName()} (NPC)",
|
||||||
CollectionType.FemaleVeenaNpc => $"Female {SubRace.Veena.ToName()} (NPC)",
|
CollectionType.FemaleVeenaNpc => $"Female {SubRace.Veena.ToName()} (NPC)",
|
||||||
CollectionType.Inactive => "Collection",
|
CollectionType.Inactive => "Collection",
|
||||||
CollectionType.Default => "Default",
|
CollectionType.Default => "Base",
|
||||||
CollectionType.Interface => "Interface",
|
CollectionType.Interface => "Interface",
|
||||||
CollectionType.Individual => "Individual",
|
CollectionType.Individual => "Individual",
|
||||||
CollectionType.Current => "Current",
|
CollectionType.Current => "Current",
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static string ToDescription(this CollectionType collectionType)
|
public static string ToDescription(this CollectionType collectionType)
|
||||||
|
|
@ -580,4 +576,4 @@ public static class CollectionTypeExtensions
|
||||||
"This collection applies to all female non-player character Veena Viera that do not have a more specific character collection associated.",
|
"This collection applies to all female non-player character Veena Viera that do not have a more specific character collection associated.",
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ public partial class ModCollection
|
||||||
/// 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 = CreateEmpty(EmptyCollectionName, 0);
|
public static readonly ModCollection Empty = CreateEmpty(EmptyCollectionName, 0, 0);
|
||||||
|
|
||||||
/// <summary> The name of a collection can not contain characters invalid in a path. </summary>
|
/// <summary> The name of a collection can not contain characters invalid in a path. </summary>
|
||||||
public string Name { get; internal init; }
|
public string Name { get; internal init; }
|
||||||
|
|
@ -133,10 +133,10 @@ public partial class ModCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Constructor for empty collections. </summary>
|
/// <summary> Constructor for empty collections. </summary>
|
||||||
public static ModCollection CreateEmpty(string name, int index)
|
public static ModCollection CreateEmpty(string name, 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(name, index, 0, CurrentVersion, new List<ModSettings?>(), new List<ModCollection>(),
|
return new ModCollection(name, index, 0, CurrentVersion, Enumerable.Repeat((ModSettings?) null, modCount).ToList(), new List<ModCollection>(),
|
||||||
new Dictionary<string, ModSettings.SavedSettings>());
|
new Dictionary<string, ModSettings.SavedSettings>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
|
|
||||||
namespace Penumbra.Interop.Services;
|
namespace Penumbra.Interop.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle font reloading via game functions.
|
/// Handle font reloading via game functions.
|
||||||
/// May cause a interface flicker while reloading.
|
/// May cause a interface flicker while reloading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public unsafe class FontReloader
|
public unsafe class FontReloader
|
||||||
{
|
{
|
||||||
|
|
@ -21,24 +21,27 @@ public unsafe class FontReloader
|
||||||
Penumbra.Log.Error("Could not reload fonts, function could not be found.");
|
Penumbra.Log.Error("Could not reload fonts, function could not be found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly AtkModule* _atkModule = null!;
|
private AtkModule* _atkModule = null!;
|
||||||
private readonly delegate* unmanaged<AtkModule*, bool, bool, void> _reloadFontsFunc = null!;
|
private delegate* unmanaged<AtkModule*, bool, bool, void> _reloadFontsFunc = null!;
|
||||||
|
|
||||||
public FontReloader()
|
public FontReloader(Dalamud.Game.Framework dFramework)
|
||||||
{
|
{
|
||||||
var framework = Framework.Instance();
|
dFramework.RunOnFrameworkThread(() =>
|
||||||
if (framework == null)
|
{
|
||||||
return;
|
var framework = Framework.Instance();
|
||||||
|
if (framework == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var uiModule = framework->GetUiModule();
|
var uiModule = framework->GetUiModule();
|
||||||
if (uiModule == null)
|
if (uiModule == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var atkModule = uiModule->GetRaptureAtkModule();
|
var atkModule = uiModule->GetRaptureAtkModule();
|
||||||
if (atkModule == null)
|
if (atkModule == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_atkModule = &atkModule->AtkModule;
|
_atkModule = &atkModule->AtkModule;
|
||||||
_reloadFontsFunc = ((delegate* unmanaged<AtkModule*, bool, bool, void>*)_atkModule->vtbl)[Offsets.ReloadFontsVfunc];
|
_reloadFontsFunc = ((delegate* unmanaged<AtkModule*, bool, bool, void>*)_atkModule->vtbl)[Offsets.ReloadFontsVfunc];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
public void Replace(ResourceHandle* resource)
|
public void Replace(ResourceHandle* resource)
|
||||||
{
|
{
|
||||||
var (data, length) = resource->GetData();
|
var (data, length) = resource->GetData();
|
||||||
var newData = Penumbra.MetaFileManager.AllocateDefaultMemory(ActualLength, 8);
|
var newData = Manager.AllocateDefaultMemory(ActualLength, 8);
|
||||||
if (newData == null)
|
if (newData == null)
|
||||||
{
|
{
|
||||||
Penumbra.Log.Error($"Could not replace loaded IMC data at 0x{(ulong)resource:X}, allocation failed.");
|
Penumbra.Log.Error($"Could not replace loaded IMC data at 0x{(ulong)resource:X}, allocation failed.");
|
||||||
|
|
@ -187,7 +187,7 @@ public unsafe class ImcFile : MetaBaseFile
|
||||||
|
|
||||||
MemoryUtility.MemCpyUnchecked(newData, Data, ActualLength);
|
MemoryUtility.MemCpyUnchecked(newData, Data, ActualLength);
|
||||||
|
|
||||||
Penumbra.MetaFileManager.Free(data, length);
|
Manager.Free(data, length);
|
||||||
resource->SetData((IntPtr)newData, ActualLength);
|
resource->SetData((IntPtr)newData, ActualLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ public unsafe class MetaFileManager
|
||||||
internal readonly CharacterUtility CharacterUtility;
|
internal readonly CharacterUtility CharacterUtility;
|
||||||
internal readonly ResidentResourceManager ResidentResources;
|
internal readonly ResidentResourceManager ResidentResources;
|
||||||
internal readonly DataManager GameData;
|
internal readonly DataManager GameData;
|
||||||
internal readonly ActiveCollections ActiveCollections;
|
internal readonly ActiveCollectionData ActiveCollections;
|
||||||
internal readonly ValidityChecker ValidityChecker;
|
internal readonly ValidityChecker ValidityChecker;
|
||||||
internal readonly IdentifierService Identifier;
|
internal readonly IdentifierService Identifier;
|
||||||
|
|
||||||
public MetaFileManager(CharacterUtility characterUtility, ResidentResourceManager residentResources, DataManager gameData,
|
public MetaFileManager(CharacterUtility characterUtility, ResidentResourceManager residentResources, DataManager gameData,
|
||||||
ActiveCollections activeCollections, Configuration config, ValidityChecker validityChecker, IdentifierService identifier)
|
ActiveCollectionData activeCollections, Configuration config, ValidityChecker validityChecker, IdentifierService identifier)
|
||||||
{
|
{
|
||||||
CharacterUtility = characterUtility;
|
CharacterUtility = characterUtility;
|
||||||
ResidentResources = residentResources;
|
ResidentResources = residentResources;
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,16 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Penumbra.GameData;
|
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
|
using Penumbra.Services;
|
||||||
|
|
||||||
namespace Penumbra.Mods.ItemSwap;
|
namespace Penumbra.Mods.ItemSwap;
|
||||||
|
|
||||||
public class ItemSwapContainer
|
public class ItemSwapContainer
|
||||||
{
|
{
|
||||||
private readonly MetaFileManager _manager;
|
private readonly MetaFileManager _manager;
|
||||||
private readonly IObjectIdentifier _identifier;
|
private readonly IdentifierService _identifier;
|
||||||
|
|
||||||
private Dictionary< Utf8GamePath, FullPath > _modRedirections = new();
|
private Dictionary< Utf8GamePath, FullPath > _modRedirections = new();
|
||||||
private HashSet< MetaManipulation > _modManipulations = new();
|
private HashSet< MetaManipulation > _modManipulations = new();
|
||||||
|
|
@ -114,7 +114,7 @@ public class ItemSwapContainer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemSwapContainer(MetaFileManager manager, IObjectIdentifier identifier)
|
public ItemSwapContainer(MetaFileManager manager, IdentifierService identifier)
|
||||||
{
|
{
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_identifier = identifier;
|
_identifier = identifier;
|
||||||
|
|
@ -136,7 +136,7 @@ public class ItemSwapContainer
|
||||||
{
|
{
|
||||||
Swaps.Clear();
|
Swaps.Clear();
|
||||||
Loaded = false;
|
Loaded = false;
|
||||||
var ret = EquipmentSwap.CreateItemSwap( _manager, _identifier, Swaps, PathResolver( collection ), MetaResolver( collection ), from, to, useRightRing, useLeftRing );
|
var ret = EquipmentSwap.CreateItemSwap( _manager, _identifier.AwaitedService, Swaps, PathResolver( collection ), MetaResolver( collection ), from, to, useRightRing, useLeftRing );
|
||||||
Loaded = true;
|
Loaded = true;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +145,7 @@ public class ItemSwapContainer
|
||||||
{
|
{
|
||||||
Swaps.Clear();
|
Swaps.Clear();
|
||||||
Loaded = false;
|
Loaded = false;
|
||||||
var ret = EquipmentSwap.CreateTypeSwap( _manager, _identifier, Swaps, PathResolver( collection ), MetaResolver( collection ), slotFrom, from, slotTo, to );
|
var ret = EquipmentSwap.CreateTypeSwap( _manager, _identifier.AwaitedService, Swaps, PathResolver( collection ), MetaResolver( collection ), slotFrom, from, slotTo, to );
|
||||||
Loaded = true;
|
Loaded = true;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,6 @@ public class ModDataEditor
|
||||||
_communicatorService = communicatorService;
|
_communicatorService = communicatorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MetaFile(Mod mod)
|
|
||||||
=> _saveService.FileNames.ModMetaPath(mod);
|
|
||||||
|
|
||||||
public string DataFile(Mod mod)
|
|
||||||
=> _saveService.FileNames.LocalDataFile(mod);
|
|
||||||
|
|
||||||
/// <summary> Create the file containing the meta information about a mod from scratch. </summary>
|
/// <summary> Create the file containing the meta information about a mod from scratch. </summary>
|
||||||
public void CreateMeta(DirectoryInfo directory, string? name, string? author, string? description, string? version,
|
public void CreateMeta(DirectoryInfo directory, string? name, string? author, string? description, string? version,
|
||||||
string? website)
|
string? website)
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ public partial class ModCreator
|
||||||
if (!file.Exists)
|
if (!file.Exists)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var rgsp = TexToolsMeta.FromRgspFile(Penumbra.MetaFileManager, file.FullName, File.ReadAllBytes(file.FullName),
|
var rgsp = TexToolsMeta.FromRgspFile(_metaFileManager, file.FullName, File.ReadAllBytes(file.FullName),
|
||||||
_config.KeepDefaultMetaChanges);
|
_config.KeepDefaultMetaChanges);
|
||||||
Penumbra.Log.Verbose(
|
Penumbra.Log.Verbose(
|
||||||
$"Incorporating {file} as racial scaling file of {rgsp.MetaManipulations.Count} manipulations {deleteString}");
|
$"Incorporating {file} as racial scaling file of {rgsp.MetaManipulations.Count} manipulations {deleteString}");
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ public class Penumbra : IDalamudPlugin
|
||||||
public static Configuration Config { get; private set; } = null!;
|
public static Configuration Config { get; private set; } = null!;
|
||||||
|
|
||||||
public static CharacterUtility CharacterUtility { get; private set; } = null!;
|
public static CharacterUtility CharacterUtility { get; private set; } = null!;
|
||||||
public static MetaFileManager MetaFileManager { get; private set; } = null!;
|
|
||||||
public static ModManager ModManager { get; private set; } = null!;
|
public static ModManager ModManager { get; private set; } = null!;
|
||||||
public static ModCacheManager ModCaches { get; private set; } = null!;
|
public static ModCacheManager ModCaches { get; private set; } = null!;
|
||||||
public static CollectionManager CollectionManager { get; private set; } = null!;
|
public static CollectionManager CollectionManager { get; private set; } = null!;
|
||||||
|
|
@ -72,7 +71,6 @@ public class Penumbra : IDalamudPlugin
|
||||||
_tmp.Services.GetRequiredService<BackupService>();
|
_tmp.Services.GetRequiredService<BackupService>();
|
||||||
Config = _tmp.Services.GetRequiredService<Configuration>();
|
Config = _tmp.Services.GetRequiredService<Configuration>();
|
||||||
CharacterUtility = _tmp.Services.GetRequiredService<CharacterUtility>();
|
CharacterUtility = _tmp.Services.GetRequiredService<CharacterUtility>();
|
||||||
MetaFileManager = _tmp.Services.GetRequiredService<MetaFileManager>();
|
|
||||||
Actors = _tmp.Services.GetRequiredService<ActorService>().AwaitedService;
|
Actors = _tmp.Services.GetRequiredService<ActorService>().AwaitedService;
|
||||||
_tempMods = _tmp.Services.GetRequiredService<TempModManager>();
|
_tempMods = _tmp.Services.GetRequiredService<TempModManager>();
|
||||||
_residentResources = _tmp.Services.GetRequiredService<ResidentResourceManager>();
|
_residentResources = _tmp.Services.GetRequiredService<ResidentResourceManager>();
|
||||||
|
|
@ -114,8 +112,6 @@ public class Penumbra : IDalamudPlugin
|
||||||
var api = _tmp.Services.GetRequiredService<IPenumbraApi>();
|
var api = _tmp.Services.GetRequiredService<IPenumbraApi>();
|
||||||
HttpApi = _tmp.Services.GetRequiredService<HttpApi>();
|
HttpApi = _tmp.Services.GetRequiredService<HttpApi>();
|
||||||
_tmp.Services.GetRequiredService<PenumbraIpcProviders>();
|
_tmp.Services.GetRequiredService<PenumbraIpcProviders>();
|
||||||
if (Config.EnableHttpApi)
|
|
||||||
HttpApi.CreateWebServer();
|
|
||||||
api.ChangedItemTooltip += it =>
|
api.ChangedItemTooltip += it =>
|
||||||
{
|
{
|
||||||
if (it is Item)
|
if (it is Item)
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ public class PenumbraNew
|
||||||
|
|
||||||
// Add Collection Services
|
// Add Collection Services
|
||||||
services.AddSingleton<CollectionStorage>()
|
services.AddSingleton<CollectionStorage>()
|
||||||
|
.AddSingleton<ActiveCollectionData>()
|
||||||
.AddSingleton<ActiveCollections>()
|
.AddSingleton<ActiveCollections>()
|
||||||
.AddSingleton<InheritanceManager>()
|
.AddSingleton<InheritanceManager>()
|
||||||
.AddSingleton<CollectionCacheManager>()
|
.AddSingleton<CollectionCacheManager>()
|
||||||
|
|
@ -139,6 +140,7 @@ public class PenumbraNew
|
||||||
.AddSingleton<ModPanelEditTab>()
|
.AddSingleton<ModPanelEditTab>()
|
||||||
.AddSingleton<ModPanelChangedItemsTab>()
|
.AddSingleton<ModPanelChangedItemsTab>()
|
||||||
.AddSingleton<ModPanelConflictsTab>()
|
.AddSingleton<ModPanelConflictsTab>()
|
||||||
|
.AddSingleton<ModPanelCollectionsTab>()
|
||||||
.AddSingleton<ModPanelTabBar>()
|
.AddSingleton<ModPanelTabBar>()
|
||||||
.AddSingleton<ModFileSystemSelector>()
|
.AddSingleton<ModFileSystemSelector>()
|
||||||
.AddSingleton<CollectionsTab>()
|
.AddSingleton<CollectionsTab>()
|
||||||
|
|
|
||||||
|
|
@ -252,11 +252,11 @@ public class ConfigMigrationService
|
||||||
using var j = new JsonTextWriter(writer);
|
using var j = new JsonTextWriter(writer);
|
||||||
j.Formatting = Formatting.Indented;
|
j.Formatting = Formatting.Indented;
|
||||||
j.WriteStartObject();
|
j.WriteStartObject();
|
||||||
j.WritePropertyName(nameof(ActiveCollections.Default));
|
j.WritePropertyName(nameof(ActiveCollectionData.Default));
|
||||||
j.WriteValue(def);
|
j.WriteValue(def);
|
||||||
j.WritePropertyName(nameof(ActiveCollections.Interface));
|
j.WritePropertyName(nameof(ActiveCollectionData.Interface));
|
||||||
j.WriteValue(ui);
|
j.WriteValue(ui);
|
||||||
j.WritePropertyName(nameof(ActiveCollections.Current));
|
j.WritePropertyName(nameof(ActiveCollectionData.Current));
|
||||||
j.WriteValue(current);
|
j.WriteValue(current);
|
||||||
foreach (var (type, collection) in special)
|
foreach (var (type, collection) in special)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -30,19 +30,17 @@ public class ItemSwapTab : IDisposable, ITab
|
||||||
private readonly ItemService _itemService;
|
private readonly ItemService _itemService;
|
||||||
private readonly CollectionManager _collectionManager;
|
private readonly CollectionManager _collectionManager;
|
||||||
private readonly ModManager _modManager;
|
private readonly ModManager _modManager;
|
||||||
private readonly Configuration _config;
|
|
||||||
private readonly MetaFileManager _metaFileManager;
|
private readonly MetaFileManager _metaFileManager;
|
||||||
|
|
||||||
public ItemSwapTab(CommunicatorService communicator, ItemService itemService, CollectionManager collectionManager,
|
public ItemSwapTab(CommunicatorService communicator, ItemService itemService, CollectionManager collectionManager,
|
||||||
ModManager modManager, Configuration config, IdentifierService identifier, MetaFileManager metaFileManager)
|
ModManager modManager, IdentifierService identifier, MetaFileManager metaFileManager)
|
||||||
{
|
{
|
||||||
_communicator = communicator;
|
_communicator = communicator;
|
||||||
_itemService = itemService;
|
_itemService = itemService;
|
||||||
_collectionManager = collectionManager;
|
_collectionManager = collectionManager;
|
||||||
_modManager = modManager;
|
_modManager = modManager;
|
||||||
_config = config;
|
|
||||||
_metaFileManager = metaFileManager;
|
_metaFileManager = metaFileManager;
|
||||||
_swapData = new ItemSwapContainer(metaFileManager, identifier.AwaitedService);
|
_swapData = new ItemSwapContainer(metaFileManager, identifier);
|
||||||
|
|
||||||
_selectors = new Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)>
|
_selectors = new Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ public static class Colors
|
||||||
public const uint MetaInfoText = 0xAAFFFFFF;
|
public const uint MetaInfoText = 0xAAFFFFFF;
|
||||||
public const uint RedTableBgTint = 0x40000080;
|
public const uint RedTableBgTint = 0x40000080;
|
||||||
public const uint DiscordColor = 0xFFDA8972;
|
public const uint DiscordColor = 0xFFDA8972;
|
||||||
|
public const uint SelectedColor = 0x6069C056;
|
||||||
|
public const uint RedundantColor = 0x6050D0D0;
|
||||||
public const uint FilterActive = 0x807070FF;
|
public const uint FilterActive = 0x807070FF;
|
||||||
public const uint TutorialMarker = 0xFF20FFFF;
|
public const uint TutorialMarker = 0xFF20FFFF;
|
||||||
public const uint TutorialBorder = 0xD00000FF;
|
public const uint TutorialBorder = 0xD00000FF;
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
namespace Penumbra.UI.CollectionTab;
|
namespace Penumbra.UI.CollectionTab;
|
||||||
|
|
||||||
public sealed class CollectionSelector : FilterComboCache<ModCollection>
|
public sealed class CollectionCombo : FilterComboCache<ModCollection>
|
||||||
{
|
{
|
||||||
private readonly CollectionManager _collectionManager;
|
private readonly CollectionManager _collectionManager;
|
||||||
|
|
||||||
public CollectionSelector(CollectionManager manager, Func<IReadOnlyList<ModCollection>> items)
|
public CollectionCombo(CollectionManager manager, Func<IReadOnlyList<ModCollection>> items)
|
||||||
: base(items)
|
: base(items)
|
||||||
=> _collectionManager = manager;
|
=> _collectionManager = manager;
|
||||||
|
|
||||||
|
|
@ -18,9 +18,9 @@ public class IndividualCollectionUi
|
||||||
{
|
{
|
||||||
private readonly ActorService _actorService;
|
private readonly ActorService _actorService;
|
||||||
private readonly CollectionManager _collectionManager;
|
private readonly CollectionManager _collectionManager;
|
||||||
private readonly CollectionSelector _withEmpty;
|
private readonly CollectionCombo _withEmpty;
|
||||||
|
|
||||||
public IndividualCollectionUi(ActorService actors, CollectionManager collectionManager, CollectionSelector withEmpty)
|
public IndividualCollectionUi(ActorService actors, CollectionManager collectionManager, CollectionCombo withEmpty)
|
||||||
{
|
{
|
||||||
_actorService = actors;
|
_actorService = actors;
|
||||||
_collectionManager = collectionManager;
|
_collectionManager = collectionManager;
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using OtterGui.Widgets;
|
using OtterGui.Widgets;
|
||||||
using Penumbra.Collections;
|
|
||||||
using Penumbra.Collections.Manager;
|
using Penumbra.Collections.Manager;
|
||||||
|
|
||||||
namespace Penumbra.UI.CollectionTab;
|
namespace Penumbra.UI.CollectionTab;
|
||||||
|
|
@ -1,299 +1,592 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Linq;
|
||||||
using Dalamud.Interface;
|
using System.Numerics;
|
||||||
using Dalamud.Interface.Components;
|
using System.Text;
|
||||||
using ImGuiNET;
|
using Dalamud.Game.ClientState.Objects;
|
||||||
using OtterGui;
|
using Dalamud.Interface;
|
||||||
using OtterGui.Raii;
|
using ImGuiNET;
|
||||||
using OtterGui.Widgets;
|
using OtterGui;
|
||||||
using Penumbra.Collections;
|
using OtterGui.Raii;
|
||||||
using Penumbra.Collections.Manager;
|
using OtterGui.Widgets;
|
||||||
using Penumbra.Services;
|
using Penumbra.Collections;
|
||||||
using Penumbra.UI.CollectionTab;
|
using Penumbra.Collections.Manager;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
namespace Penumbra.UI.Tabs;
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.Mods.Manager;
|
||||||
public class CollectionsTab : IDisposable, ITab
|
using Penumbra.Services;
|
||||||
{
|
using Penumbra.UI.Classes;
|
||||||
private readonly CommunicatorService _communicator;
|
using Penumbra.UI.CollectionTab;
|
||||||
private readonly Configuration _config;
|
|
||||||
private readonly CollectionManager _collectionManager;
|
namespace Penumbra.UI.Tabs;
|
||||||
private readonly TutorialService _tutorial;
|
|
||||||
private readonly SpecialCombo _specialCollectionCombo;
|
public sealed class CollectionTree
|
||||||
|
{
|
||||||
private readonly CollectionSelector _collectionsWithEmpty;
|
private readonly CollectionStorage _collections;
|
||||||
private readonly CollectionSelector _collectionSelector;
|
private readonly ActiveCollections _active;
|
||||||
private readonly InheritanceUi _inheritance;
|
private readonly CollectionSelector2 _selector;
|
||||||
private readonly IndividualCollectionUi _individualCollections;
|
private readonly ActorService _actors;
|
||||||
|
private readonly TargetManager _targets;
|
||||||
public CollectionsTab(ActorService actorService, CommunicatorService communicator, CollectionManager collectionManager,
|
|
||||||
TutorialService tutorial, Configuration config)
|
private static readonly IReadOnlyList<(string Name, uint Border)> Buttons = CreateButtons();
|
||||||
{
|
private static readonly IReadOnlyList<(CollectionType, bool, bool, string, uint)> AdvancedTree = CreateTree();
|
||||||
_communicator = communicator;
|
|
||||||
_collectionManager = collectionManager;
|
public CollectionTree(CollectionManager manager, CollectionSelector2 selector, ActorService actors,
|
||||||
_tutorial = tutorial;
|
TargetManager targets)
|
||||||
_config = config;
|
{
|
||||||
_specialCollectionCombo = new SpecialCombo(_collectionManager, "##NewSpecial", 350);
|
_collections = manager.Storage;
|
||||||
_collectionsWithEmpty = new CollectionSelector(_collectionManager,
|
_active = manager.Active;
|
||||||
() => _collectionManager.Storage.OrderBy(c => c.Name).Prepend(ModCollection.Empty).ToList());
|
_selector = selector;
|
||||||
_collectionSelector = new CollectionSelector(_collectionManager, () => _collectionManager.Storage.OrderBy(c => c.Name).ToList());
|
_actors = actors;
|
||||||
_inheritance = new InheritanceUi(_collectionManager);
|
_targets = targets;
|
||||||
_individualCollections = new IndividualCollectionUi(actorService, _collectionManager, _collectionsWithEmpty);
|
}
|
||||||
|
|
||||||
_communicator.CollectionChange.Subscribe(_individualCollections.UpdateIdentifiers);
|
public void DrawSimple()
|
||||||
}
|
{
|
||||||
|
var buttonWidth = new Vector2(200 * ImGuiHelpers.GlobalScale, 2 * ImGui.GetTextLineHeightWithSpacing());
|
||||||
public ReadOnlySpan<byte> Label
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, Vector2.Zero)
|
||||||
=> "Collections"u8;
|
.Push(ImGuiStyleVar.FrameBorderSize, 1 * ImGuiHelpers.GlobalScale);
|
||||||
|
DrawSimpleCollectionButton(CollectionType.Default, buttonWidth);
|
||||||
/// <summary> Draw a collection selector of a certain width for a certain type. </summary>
|
DrawSimpleCollectionButton(CollectionType.Interface, buttonWidth);
|
||||||
public void DrawCollectionSelector(string label, float width, CollectionType collectionType, bool withEmpty)
|
DrawSimpleCollectionButton(CollectionType.Yourself, buttonWidth);
|
||||||
=> (withEmpty ? _collectionsWithEmpty : _collectionSelector).Draw(label, width, collectionType);
|
DrawSimpleCollectionButton(CollectionType.MalePlayerCharacter, buttonWidth);
|
||||||
|
DrawSimpleCollectionButton(CollectionType.FemalePlayerCharacter, buttonWidth);
|
||||||
public void Dispose()
|
DrawSimpleCollectionButton(CollectionType.MaleNonPlayerCharacter, buttonWidth);
|
||||||
=> _communicator.CollectionChange.Unsubscribe(_individualCollections.UpdateIdentifiers);
|
DrawSimpleCollectionButton(CollectionType.FemaleNonPlayerCharacter, buttonWidth);
|
||||||
|
|
||||||
/// <summary> Draw a tutorial step regardless of tab selection. </summary>
|
var specialWidth = buttonWidth with { X = 275 * ImGuiHelpers.GlobalScale };
|
||||||
public void DrawHeader()
|
var player = _actors.AwaitedService.GetCurrentPlayer();
|
||||||
=> _tutorial.OpenTutorial(BasicTutorialSteps.Collections);
|
DrawButton($"Current Character ({(player.IsValid ? player.ToString() : "Unavailable")})", CollectionType.Individual, specialWidth, 0,
|
||||||
|
player);
|
||||||
public void DrawContent()
|
ImGui.SameLine();
|
||||||
{
|
|
||||||
using var child = ImRaii.Child("##collections", -Vector2.One);
|
var target = _actors.AwaitedService.FromObject(_targets.Target, false, true, true);
|
||||||
if (child)
|
DrawButton($"Current Target ({(target.IsValid ? target.ToString() : "Unavailable")})", CollectionType.Individual, specialWidth, 0, target);
|
||||||
{
|
if (_active.Individuals.Count > 0)
|
||||||
DrawActiveCollectionSelectors();
|
{
|
||||||
DrawMainSelectors();
|
ImGui.TextUnformatted("Currently Active Individual Assignments");
|
||||||
}
|
for (var i = 0; i < _active.Individuals.Count; ++i)
|
||||||
}
|
{
|
||||||
|
var (name, ids, coll) = _active.Individuals.Assignments[i];
|
||||||
#region New Collections
|
DrawButton(name, CollectionType.Individual, buttonWidth, 0, ids[0], coll);
|
||||||
|
|
||||||
// Input text fields.
|
ImGui.SameLine();
|
||||||
private string _newCollectionName = string.Empty;
|
if (ImGui.GetContentRegionAvail().X < buttonWidth.X + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X
|
||||||
private bool _canAddCollection;
|
&& i < _active.Individuals.Count - 1)
|
||||||
|
ImGui.NewLine();
|
||||||
/// <summary>
|
}
|
||||||
/// Create a new collection that is either empty or a duplicate of the current collection.
|
|
||||||
/// Resets the new collection name.
|
ImGui.NewLine();
|
||||||
/// </summary>
|
}
|
||||||
private void CreateNewCollection(bool duplicate)
|
|
||||||
{
|
var first = true;
|
||||||
if (_collectionManager.Storage.AddCollection(_newCollectionName, duplicate ? _collectionManager.Active.Current : null))
|
|
||||||
_newCollectionName = string.Empty;
|
void Button(CollectionType type)
|
||||||
}
|
{
|
||||||
|
var (name, border) = Buttons[(int)type];
|
||||||
/// <summary> Draw the Clean Unused Settings button if there are any. </summary>
|
var collection = _active.ByType(type);
|
||||||
private void DrawCleanCollectionButton(Vector2 width)
|
if (collection == null)
|
||||||
{
|
return;
|
||||||
if (_collectionManager.Active.Current.UnusedSettings.Count == 0)
|
|
||||||
return;
|
if (first)
|
||||||
|
{
|
||||||
ImGui.SameLine();
|
ImGui.Separator();
|
||||||
if (ImGuiUtil.DrawDisabledButton(
|
ImGui.TextUnformatted("Currently Active Advanced Assignments");
|
||||||
$"Clean {_collectionManager.Active.Current.UnusedSettings.Count} Unused Settings###CleanSettings", width
|
first = false;
|
||||||
, "Remove all stored settings for mods not currently available and fix invalid settings.\n\nUse at own risk."
|
}
|
||||||
, false))
|
DrawButton(name, type, buttonWidth, border, ActorIdentifier.Invalid, collection);
|
||||||
_collectionManager.Storage.CleanUnavailableSettings(_collectionManager.Active.Current);
|
ImGui.SameLine();
|
||||||
}
|
if (ImGui.GetContentRegionAvail().X < buttonWidth.X + ImGui.GetStyle().ItemSpacing.X + ImGui.GetStyle().WindowPadding.X)
|
||||||
|
ImGui.NewLine();
|
||||||
/// <summary> Draw the new collection input as well as its buttons. </summary>
|
}
|
||||||
private void DrawNewCollectionInput(Vector2 width)
|
|
||||||
{
|
Button(CollectionType.NonPlayerChild);
|
||||||
// Input for new collection name. Also checks for validity when changed.
|
Button(CollectionType.NonPlayerElderly);
|
||||||
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X);
|
foreach (var race in Enum.GetValues<SubRace>().Skip(1))
|
||||||
if (ImGui.InputTextWithHint("##New Collection", "New Collection Name...", ref _newCollectionName, 64))
|
{
|
||||||
_canAddCollection = _collectionManager.Storage.CanAddCollection(_newCollectionName, out _);
|
Button(CollectionTypeExtensions.FromParts(race, Gender.Male, false));
|
||||||
|
Button(CollectionTypeExtensions.FromParts(race, Gender.Female, false));
|
||||||
ImGui.SameLine();
|
Button(CollectionTypeExtensions.FromParts(race, Gender.Male, true));
|
||||||
ImGuiComponents.HelpMarker(
|
Button(CollectionTypeExtensions.FromParts(race, Gender.Female, true));
|
||||||
"A collection is a set of settings for your installed mods, including their enabled status, their priorities and their mod-specific configuration.\n"
|
}
|
||||||
+ "You can use multiple collections to quickly switch between sets of enabled mods.");
|
}
|
||||||
|
|
||||||
// Creation buttons.
|
public void DrawAdvanced()
|
||||||
var tt = _canAddCollection
|
{
|
||||||
? string.Empty
|
using var table = ImRaii.Table("##advanced", 4, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
|
||||||
: "Please enter a unique name only consisting of symbols valid in a path but no '|' before creating a collection.";
|
if (!table)
|
||||||
if (ImGuiUtil.DrawDisabledButton("Create Empty Collection", width, tt, !_canAddCollection))
|
return;
|
||||||
CreateNewCollection(false);
|
|
||||||
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, Vector2.Zero)
|
||||||
ImGui.SameLine();
|
.Push(ImGuiStyleVar.FrameBorderSize, 1 * ImGuiHelpers.GlobalScale);
|
||||||
if (ImGuiUtil.DrawDisabledButton($"Duplicate {TutorialService.SelectedCollection}", width, tt, !_canAddCollection))
|
|
||||||
CreateNewCollection(true);
|
var buttonWidth = new Vector2(150 * ImGuiHelpers.GlobalScale, 2 * ImGui.GetTextLineHeightWithSpacing());
|
||||||
}
|
var dummy = new Vector2(1, 0);
|
||||||
|
|
||||||
#endregion
|
foreach (var (type, pre, post, name, border) in AdvancedTree)
|
||||||
|
{
|
||||||
#region Collection Selection
|
ImGui.TableNextColumn();
|
||||||
|
if (type is CollectionType.Inactive)
|
||||||
/// <summary> Draw all collection assignment selections. </summary>
|
continue;
|
||||||
private void DrawActiveCollectionSelectors()
|
|
||||||
{
|
if (pre)
|
||||||
UiHelpers.DefaultLineSpace();
|
ImGui.Dummy(dummy);
|
||||||
var open = ImGui.CollapsingHeader(TutorialService.ActiveCollections, ImGuiTreeNodeFlags.DefaultOpen);
|
DrawAssignmentButton(type, buttonWidth, name, border);
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.ActiveCollections);
|
if (post)
|
||||||
if (!open)
|
ImGui.Dummy(dummy);
|
||||||
return;
|
}
|
||||||
|
}
|
||||||
UiHelpers.DefaultLineSpace();
|
|
||||||
|
private void DrawContext(bool open, ModCollection? collection, CollectionType type, ActorIdentifier identifier, char suffix = 'i')
|
||||||
DrawDefaultCollectionSelector();
|
{
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.DefaultCollection);
|
var label = $"{type}{identifier}{suffix}";
|
||||||
DrawInterfaceCollectionSelector();
|
if (open)
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.InterfaceCollection);
|
ImGui.OpenPopup(label);
|
||||||
UiHelpers.DefaultLineSpace();
|
|
||||||
|
using var context = ImRaii.Popup(label);
|
||||||
DrawSpecialAssignments();
|
if (context)
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.SpecialCollections1);
|
{
|
||||||
UiHelpers.DefaultLineSpace();
|
using (var color = ImRaii.PushColor(ImGuiCol.Text, Colors.DiscordColor))
|
||||||
|
{
|
||||||
_individualCollections.Draw();
|
if (ImGui.MenuItem("Use no mods."))
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.SpecialCollections2);
|
_active.SetCollection(ModCollection.Empty, type, _active.Individuals.GetGroup(identifier));
|
||||||
UiHelpers.DefaultLineSpace();
|
}
|
||||||
}
|
|
||||||
|
if (collection != null)
|
||||||
private void DrawCurrentCollectionSelector(Vector2 width)
|
{
|
||||||
{
|
using var color = ImRaii.PushColor(ImGuiCol.Text, Colors.RegexWarningBorder);
|
||||||
using var group = ImRaii.Group();
|
if (ImGui.MenuItem("Remove this assignment."))
|
||||||
DrawCollectionSelector("##current", UiHelpers.InputTextWidth.X, CollectionType.Current, false);
|
_active.SetCollection(null, type, _active.Individuals.GetGroup(identifier));
|
||||||
ImGui.SameLine();
|
}
|
||||||
ImGuiUtil.LabeledHelpMarker(TutorialService.SelectedCollection,
|
|
||||||
"This collection will be modified when using the Installed Mods tab and making changes.\nIt is not automatically assigned to anything.");
|
foreach (var coll in _collections)
|
||||||
|
{
|
||||||
// Deletion conditions.
|
if (coll != collection && ImGui.MenuItem($"Use {coll.Name}."))
|
||||||
var deleteCondition = _collectionManager.Active.Current.Name != ModCollection.DefaultCollectionName;
|
_active.SetCollection(coll, type, _active.Individuals.GetGroup(identifier));
|
||||||
var modifierHeld = Penumbra.Config.DeleteModModifier.IsActive();
|
}
|
||||||
var tt = deleteCondition
|
}
|
||||||
? modifierHeld ? string.Empty : $"Hold {_config.DeleteModModifier} while clicking to delete the collection."
|
}
|
||||||
: $"You can not delete the collection {ModCollection.DefaultCollectionName}.";
|
|
||||||
|
private bool DrawButton(string text, CollectionType type, Vector2 width, uint borderColor, ActorIdentifier id, ModCollection? collection = null)
|
||||||
if (ImGuiUtil.DrawDisabledButton($"Delete {TutorialService.SelectedCollection}", width, tt, !deleteCondition || !modifierHeld))
|
{
|
||||||
_collectionManager.Storage.RemoveCollection(_collectionManager.Active.Current);
|
using var group = ImRaii.Group();
|
||||||
|
var invalid = type == CollectionType.Individual && !id.IsValid;
|
||||||
DrawCleanCollectionButton(width);
|
var redundancy = _active.RedundancyCheck(type, id);
|
||||||
}
|
collection ??= _active.ByType(type, id);
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Button,
|
||||||
/// <summary> Draw the selector for the default collection assignment. </summary>
|
collection == null
|
||||||
private void DrawDefaultCollectionSelector()
|
? 0
|
||||||
{
|
: redundancy.Length > 0
|
||||||
using var group = ImRaii.Group();
|
? Colors.RedundantColor
|
||||||
DrawCollectionSelector("##default", UiHelpers.InputTextWidth.X, CollectionType.Default, true);
|
: collection == _active.Current
|
||||||
ImGui.SameLine();
|
? Colors.SelectedColor
|
||||||
ImGuiUtil.LabeledHelpMarker(TutorialService.DefaultCollection,
|
: collection == ModCollection.Empty
|
||||||
$"Mods in the {TutorialService.DefaultCollection} are loaded for anything that is not associated with the user interface or a character in the game,"
|
? Colors.RedTableBgTint
|
||||||
+ "as well as any character for whom no more specific conditions from below apply.");
|
: ImGui.GetColorU32(ImGuiCol.Button), !invalid)
|
||||||
}
|
.Push(ImGuiCol.Border, borderColor == 0 ? ImGui.GetColorU32(ImGuiCol.TextDisabled) : borderColor);
|
||||||
|
using var disabled = ImRaii.Disabled(invalid);
|
||||||
/// <summary> Draw the selector for the interface collection assignment. </summary>
|
var button = ImGui.Button(text, width) || ImGui.IsItemClicked(ImGuiMouseButton.Right);
|
||||||
private void DrawInterfaceCollectionSelector()
|
var hovered = redundancy.Length > 0 && ImGui.IsItemHovered();
|
||||||
{
|
if (!invalid)
|
||||||
using var group = ImRaii.Group();
|
{
|
||||||
DrawCollectionSelector("##interface", UiHelpers.InputTextWidth.X, CollectionType.Interface, true);
|
_selector.DragTarget(type, id);
|
||||||
ImGui.SameLine();
|
var name = collection == ModCollection.Empty ? "Use No Mods" : collection?.Name ?? "Unassigned";
|
||||||
ImGuiUtil.LabeledHelpMarker(TutorialService.InterfaceCollection,
|
var size = ImGui.CalcTextSize(name);
|
||||||
$"Mods in the {TutorialService.InterfaceCollection} are loaded for any file that the game categorizes as an UI file. This is mostly icons as well as the tiles that generate the user interface windows themselves.");
|
var textPos = ImGui.GetItemRectMax() - size - ImGui.GetStyle().FramePadding;
|
||||||
}
|
ImGui.GetWindowDrawList().AddText(textPos, ImGui.GetColorU32(ImGuiCol.Text), name);
|
||||||
|
DrawContext(button, collection, type, id);
|
||||||
/// <summary> Description for character groups used in multiple help markers. </summary>
|
}
|
||||||
private const string CharacterGroupDescription =
|
|
||||||
$"{TutorialService.CharacterGroups} apply to certain types of characters based on a condition.\n"
|
if (hovered)
|
||||||
+ $"All of them take precedence before the {TutorialService.DefaultCollection},\n"
|
ImGui.SetTooltip(redundancy);
|
||||||
+ $"but all {TutorialService.IndividualAssignments} take precedence before them.";
|
|
||||||
|
return button;
|
||||||
/// <summary> Draw the entire group assignment section. </summary>
|
}
|
||||||
private void DrawSpecialAssignments()
|
|
||||||
{
|
private void DrawSimpleCollectionButton(CollectionType type, Vector2 width)
|
||||||
using var _ = ImRaii.Group();
|
{
|
||||||
ImGui.AlignTextToFramePadding();
|
DrawButton(type.ToName(), type, width, 0, ActorIdentifier.Invalid);
|
||||||
ImGui.TextUnformatted(TutorialService.CharacterGroups);
|
ImGui.SameLine();
|
||||||
ImGuiComponents.HelpMarker(CharacterGroupDescription);
|
var secondLine = string.Empty;
|
||||||
ImGui.Separator();
|
foreach (var parent in type.InheritanceOrder())
|
||||||
DrawSpecialCollections();
|
{
|
||||||
ImGui.Dummy(Vector2.Zero);
|
var coll = _active.ByType(parent);
|
||||||
DrawNewSpecialCollection();
|
if (coll == null)
|
||||||
}
|
continue;
|
||||||
|
|
||||||
/// <summary> Draw a new combo to select special collections as well as button to create it. </summary>
|
secondLine = $"\nWill behave as {parent.ToName()} ({coll.Name}) while unassigned.";
|
||||||
private void DrawNewSpecialCollection()
|
break;
|
||||||
{
|
}
|
||||||
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X);
|
|
||||||
if (_specialCollectionCombo.CurrentIdx == -1
|
ImGui.TextUnformatted(type.ToDescription() + secondLine);
|
||||||
|| _collectionManager.Active.ByType(_specialCollectionCombo.CurrentType!.Value.Item1) != null)
|
ImGui.Separator();
|
||||||
{
|
}
|
||||||
_specialCollectionCombo.ResetFilter();
|
|
||||||
_specialCollectionCombo.CurrentIdx = CollectionTypeExtensions.Special
|
private void DrawAssignmentButton(CollectionType type, Vector2 width, string name, uint color)
|
||||||
.IndexOf(t => _collectionManager.Active.ByType(t.Item1) == null);
|
=> DrawButton(name, type, width, color, ActorIdentifier.Invalid, _active.ByType(type));
|
||||||
}
|
|
||||||
|
private static IReadOnlyList<(string Name, uint Border)> CreateButtons()
|
||||||
if (_specialCollectionCombo.CurrentType == null)
|
{
|
||||||
return;
|
var ret = Enum.GetValues<CollectionType>().Select(t => (t.ToName(), 0u)).ToArray();
|
||||||
|
|
||||||
_specialCollectionCombo.Draw();
|
foreach (var race in Enum.GetValues<SubRace>().Skip(1))
|
||||||
ImGui.SameLine();
|
{
|
||||||
var disabled = _specialCollectionCombo.CurrentType == null;
|
var color = race switch
|
||||||
var tt = disabled
|
{
|
||||||
? $"Please select a condition for a {TutorialService.GroupAssignment} before creating the collection.\n\n"
|
SubRace.Midlander => 0xAA5C9FE4u,
|
||||||
+ CharacterGroupDescription
|
SubRace.Highlander => 0xAA5C9FE4u,
|
||||||
: CharacterGroupDescription;
|
SubRace.Wildwood => 0xAA5C9F49u,
|
||||||
if (!ImGuiUtil.DrawDisabledButton($"Assign {TutorialService.ConditionalGroup}", new Vector2(120 * UiHelpers.Scale, 0), tt, disabled))
|
SubRace.Duskwight => 0xAA5C9F49u,
|
||||||
return;
|
SubRace.Plainsfolk => 0xAAEF8CB6u,
|
||||||
|
SubRace.Dunesfolk => 0xAAEF8CB6u,
|
||||||
_collectionManager.Active.CreateSpecialCollection(_specialCollectionCombo.CurrentType!.Value.Item1);
|
SubRace.SeekerOfTheSun => 0xAA8CEFECu,
|
||||||
_specialCollectionCombo.CurrentIdx = -1;
|
SubRace.KeeperOfTheMoon => 0xAA8CEFECu,
|
||||||
}
|
SubRace.Seawolf => 0xAAEFE68Cu,
|
||||||
|
SubRace.Hellsguard => 0xAAEFE68Cu,
|
||||||
#endregion
|
SubRace.Raen => 0xAAB5EF8Cu,
|
||||||
|
SubRace.Xaela => 0xAAB5EF8Cu,
|
||||||
#region Current Collection Editing
|
SubRace.Helion => 0xAAFFFFFFu,
|
||||||
|
SubRace.Lost => 0xAAFFFFFFu,
|
||||||
/// <summary> Draw the current collection selection, the creation of new collections and the inheritance block. </summary>
|
SubRace.Rava => 0xAA607FA7u,
|
||||||
private void DrawMainSelectors()
|
SubRace.Veena => 0xAA607FA7u,
|
||||||
{
|
_ => 0u,
|
||||||
UiHelpers.DefaultLineSpace();
|
};
|
||||||
var open = ImGui.CollapsingHeader("Collection Settings", ImGuiTreeNodeFlags.DefaultOpen);
|
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.EditingCollections);
|
ret[(int)CollectionTypeExtensions.FromParts(race, Gender.Male, false)] = ($"♂ {race.ToShortName()}", color);
|
||||||
if (!open)
|
ret[(int)CollectionTypeExtensions.FromParts(race, Gender.Female, false)] = ($"♀ {race.ToShortName()}", color);
|
||||||
return;
|
ret[(int)CollectionTypeExtensions.FromParts(race, Gender.Male, true)] = ($"♂ {race.ToShortName()} (NPC)", color);
|
||||||
|
ret[(int)CollectionTypeExtensions.FromParts(race, Gender.Female, true)] = ($"♀ {race.ToShortName()} (NPC)", color);
|
||||||
var width = new Vector2((UiHelpers.InputTextWidth.X - ImGui.GetStyle().ItemSpacing.X) / 2, 0);
|
}
|
||||||
UiHelpers.DefaultLineSpace();
|
|
||||||
|
ret[(int)CollectionType.MalePlayerCharacter] = ("♂ Player", 0);
|
||||||
DrawCurrentCollectionSelector(width);
|
ret[(int)CollectionType.FemalePlayerCharacter] = ("♀ Player", 0);
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.CurrentCollection);
|
ret[(int)CollectionType.MaleNonPlayerCharacter] = ("♂ NPC", 0);
|
||||||
UiHelpers.DefaultLineSpace();
|
ret[(int)CollectionType.FemaleNonPlayerCharacter] = ("♀ NPC", 0);
|
||||||
|
return ret;
|
||||||
DrawNewCollectionInput(width);
|
}
|
||||||
UiHelpers.DefaultLineSpace();
|
|
||||||
|
private static IReadOnlyList<(CollectionType, bool, bool, string, uint)> CreateTree()
|
||||||
_inheritance.Draw();
|
{
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.Inheritance);
|
var ret = new List<(CollectionType, bool, bool, string, uint)>(Buttons.Count);
|
||||||
}
|
|
||||||
|
void Add(CollectionType type, bool pre, bool post)
|
||||||
/// <summary> Draw all currently set special collections. </summary>
|
{
|
||||||
private void DrawSpecialCollections()
|
var (name, border) = (int)type >= Buttons.Count ? (type.ToName(), 0) : Buttons[(int)type];
|
||||||
{
|
ret.Add((type, pre, post, name, border));
|
||||||
foreach (var (type, name, desc) in CollectionTypeExtensions.Special)
|
}
|
||||||
{
|
|
||||||
var collection = _collectionManager.Active.ByType(type);
|
Add(CollectionType.Default, false, false);
|
||||||
if (collection == null)
|
Add(CollectionType.Interface, false, false);
|
||||||
continue;
|
Add(CollectionType.Inactive, false, false);
|
||||||
|
Add(CollectionType.Inactive, false, false);
|
||||||
using var id = ImRaii.PushId((int)type);
|
Add(CollectionType.Yourself, false, true);
|
||||||
DrawCollectionSelector("##SpecialCombo", UiHelpers.InputTextWidth.X, type, true);
|
Add(CollectionType.Inactive, false, true);
|
||||||
ImGui.SameLine();
|
Add(CollectionType.NonPlayerChild, false, true);
|
||||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), UiHelpers.IconButtonSize, string.Empty,
|
Add(CollectionType.NonPlayerElderly, false, true);
|
||||||
false, true))
|
Add(CollectionType.MalePlayerCharacter, true, true);
|
||||||
{
|
Add(CollectionType.FemalePlayerCharacter, true, true);
|
||||||
_collectionManager.Active.RemoveSpecialCollection(type);
|
Add(CollectionType.MaleNonPlayerCharacter, true, true);
|
||||||
_specialCollectionCombo.ResetFilter();
|
Add(CollectionType.FemaleNonPlayerCharacter, true, true);
|
||||||
}
|
var pre = true;
|
||||||
|
foreach (var race in Enum.GetValues<SubRace>().Skip(1))
|
||||||
ImGui.SameLine();
|
{
|
||||||
ImGui.AlignTextToFramePadding();
|
Add(CollectionTypeExtensions.FromParts(race, Gender.Male, false), pre, !pre);
|
||||||
ImGuiUtil.LabeledHelpMarker(name, desc);
|
Add(CollectionTypeExtensions.FromParts(race, Gender.Female, false), pre, !pre);
|
||||||
}
|
Add(CollectionTypeExtensions.FromParts(race, Gender.Male, true), pre, !pre);
|
||||||
}
|
Add(CollectionTypeExtensions.FromParts(race, Gender.Female, true), pre, !pre);
|
||||||
|
pre = !pre;
|
||||||
#endregion
|
}
|
||||||
}
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CollectionPanel
|
||||||
|
{
|
||||||
|
private readonly CollectionManager _manager;
|
||||||
|
private readonly ModStorage _modStorage;
|
||||||
|
private readonly InheritanceUi _inheritanceUi;
|
||||||
|
|
||||||
|
public CollectionPanel(CollectionManager manager, ModStorage modStorage)
|
||||||
|
{
|
||||||
|
_manager = manager;
|
||||||
|
_modStorage = modStorage;
|
||||||
|
_inheritanceUi = new InheritanceUi(_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
var collection = _manager.Active.Current;
|
||||||
|
DrawName(collection);
|
||||||
|
DrawStatistics(collection);
|
||||||
|
_inheritanceUi.Draw();
|
||||||
|
DrawSettingsList(collection);
|
||||||
|
DrawInactiveSettingsList(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawName(ModCollection collection)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted($"{collection.Name} ({collection.AnonymizedName})");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawStatistics(ModCollection collection)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("Used for:");
|
||||||
|
var sb = new StringBuilder(128);
|
||||||
|
if (_manager.Active.Default == collection)
|
||||||
|
sb.Append(CollectionType.Default.ToName()).Append(", ");
|
||||||
|
if (_manager.Active.Interface == collection)
|
||||||
|
sb.Append(CollectionType.Interface.ToName()).Append(", ");
|
||||||
|
foreach (var (type, _) in _manager.Active.SpecialAssignments.Where(p => p.Value == collection))
|
||||||
|
sb.Append(type.ToName()).Append(", ");
|
||||||
|
foreach (var (name, _) in _manager.Active.Individuals.Where(p => p.Collection == collection))
|
||||||
|
sb.Append(name).Append(", ");
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiUtil.TextWrapped(sb.Length == 0 ? "Nothing" : sb.ToString(0, sb.Length - 2));
|
||||||
|
|
||||||
|
if (collection.DirectParentOf.Count > 0)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("Inherited by:");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiUtil.TextWrapped(string.Join(", ", collection.DirectParentOf.Select(c => c.Name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSettingsList(ModCollection collection)
|
||||||
|
{
|
||||||
|
using var box = ImRaii.ListBox("##activeSettings");
|
||||||
|
if (!box)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var (mod, (settings, parent)) in _modStorage.Select(m => (m, collection[m.Index])).Where(t => t.Item2.Settings != null)
|
||||||
|
.OrderBy(t => t.m.Name))
|
||||||
|
ImGui.TextUnformatted($"{mod}{(parent != collection ? $" (inherited from {parent.Name})" : string.Empty)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawInactiveSettingsList(ModCollection collection)
|
||||||
|
{
|
||||||
|
if (collection.UnusedSettings.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ImGui.Button("Clear Unused Settings"))
|
||||||
|
_manager.Storage.CleanUnavailableSettings(collection);
|
||||||
|
|
||||||
|
using var box = ImRaii.ListBox("##inactiveSettings");
|
||||||
|
if (!box)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var name in collection.UnusedSettings.Keys)
|
||||||
|
ImGui.TextUnformatted(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class CollectionSelector2 : ItemSelector<ModCollection>, IDisposable
|
||||||
|
{
|
||||||
|
private readonly Configuration _config;
|
||||||
|
private readonly CommunicatorService _communicator;
|
||||||
|
private readonly CollectionStorage _storage;
|
||||||
|
private readonly ActiveCollections _active;
|
||||||
|
|
||||||
|
private ModCollection? _dragging;
|
||||||
|
|
||||||
|
public CollectionSelector2(Configuration config, CommunicatorService communicator, CollectionStorage storage, ActiveCollections active)
|
||||||
|
: base(new List<ModCollection>(), Flags.Delete | Flags.Add | Flags.Duplicate | Flags.Filter)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
_communicator = communicator;
|
||||||
|
_storage = storage;
|
||||||
|
_active = active;
|
||||||
|
|
||||||
|
_communicator.CollectionChange.Subscribe(OnCollectionChange);
|
||||||
|
// Set items.
|
||||||
|
OnCollectionChange(CollectionType.Inactive, null, null, string.Empty);
|
||||||
|
// Set selection.
|
||||||
|
OnCollectionChange(CollectionType.Current, null, _active.Current, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDelete(int idx)
|
||||||
|
{
|
||||||
|
if (idx < 0 || idx >= Items.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _storage.RemoveCollection(Items[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool DeleteButtonEnabled()
|
||||||
|
=> _storage.DefaultNamed != Current && _config.DeleteModModifier.IsActive();
|
||||||
|
|
||||||
|
protected override string DeleteButtonTooltip()
|
||||||
|
=> _storage.DefaultNamed == Current
|
||||||
|
? $"The selected collection {Current.Name} can not be deleted."
|
||||||
|
: $"Delete the currently selected collection {Current?.Name}. Hold {_config.DeleteModModifier} to delete.";
|
||||||
|
|
||||||
|
protected override bool OnAdd(string name)
|
||||||
|
=> _storage.AddCollection(name, null);
|
||||||
|
|
||||||
|
protected override bool OnDuplicate(string name, int idx)
|
||||||
|
{
|
||||||
|
if (idx < 0 || idx >= Items.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _storage.AddCollection(name, Items[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool Filtered(int idx)
|
||||||
|
=> !Items[idx].Name.Contains(Filter, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
protected override bool OnDraw(int idx)
|
||||||
|
{
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Header, Colors.SelectedColor);
|
||||||
|
var ret = ImGui.Selectable(Items[idx].Name, idx == CurrentIdx);
|
||||||
|
using var source = ImRaii.DragDropSource();
|
||||||
|
if (source)
|
||||||
|
{
|
||||||
|
_dragging = Items[idx];
|
||||||
|
ImGui.SetDragDropPayload("Assignment", nint.Zero, 0);
|
||||||
|
ImGui.TextUnformatted($"Assigning {_dragging.Name} to...");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
_active.SetCollection(Items[idx], CollectionType.Current);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DragTarget(CollectionType type, ActorIdentifier identifier)
|
||||||
|
{
|
||||||
|
using var target = ImRaii.DragDropTarget();
|
||||||
|
if (!target.Success || _dragging == null || !ImGuiUtil.IsDropping("Assignment"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_active.SetCollection(_dragging, type, _active.Individuals.GetGroup(identifier));
|
||||||
|
_dragging = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_communicator.CollectionChange.Unsubscribe(OnCollectionChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCollectionChange(CollectionType type, ModCollection? old, ModCollection? @new, string _3)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CollectionType.Temporary: return;
|
||||||
|
case CollectionType.Current:
|
||||||
|
if (@new != null)
|
||||||
|
SetCurrent(@new);
|
||||||
|
SetFilterDirty();
|
||||||
|
return;
|
||||||
|
case CollectionType.Inactive:
|
||||||
|
Items.Clear();
|
||||||
|
foreach (var c in _storage.OrderBy(c => c.Name))
|
||||||
|
Items.Add(c);
|
||||||
|
|
||||||
|
if (old == Current)
|
||||||
|
ClearCurrentSelection();
|
||||||
|
else
|
||||||
|
TryRestoreCurrent();
|
||||||
|
SetFilterDirty();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
SetFilterDirty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CollectionsTab : IDisposable, ITab
|
||||||
|
{
|
||||||
|
private readonly CommunicatorService _communicator;
|
||||||
|
private readonly Configuration _configuration;
|
||||||
|
private readonly CollectionManager _collectionManager;
|
||||||
|
private readonly CollectionSelector2 _selector;
|
||||||
|
private readonly CollectionPanel _panel;
|
||||||
|
private readonly CollectionTree _tree;
|
||||||
|
|
||||||
|
public enum PanelMode
|
||||||
|
{
|
||||||
|
SimpleAssignment,
|
||||||
|
ComplexAssignment,
|
||||||
|
Details,
|
||||||
|
};
|
||||||
|
|
||||||
|
public PanelMode Mode = PanelMode.SimpleAssignment;
|
||||||
|
|
||||||
|
public CollectionsTab(CommunicatorService communicator, Configuration configuration, CollectionManager collectionManager,
|
||||||
|
ModStorage modStorage, ActorService actors, TargetManager targets)
|
||||||
|
{
|
||||||
|
_communicator = communicator;
|
||||||
|
_configuration = configuration;
|
||||||
|
_collectionManager = collectionManager;
|
||||||
|
_selector = new CollectionSelector2(_configuration, _communicator, _collectionManager.Storage, _collectionManager.Active);
|
||||||
|
_panel = new CollectionPanel(_collectionManager, modStorage);
|
||||||
|
_tree = new CollectionTree(collectionManager, _selector, actors, targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_selector.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> Label
|
||||||
|
=> "Collections"u8;
|
||||||
|
|
||||||
|
public void DrawContent()
|
||||||
|
{
|
||||||
|
var width = ImGui.CalcTextSize("nnnnnnnnnnnnnnnnnnnnnnnn").X;
|
||||||
|
_selector.Draw(width);
|
||||||
|
ImGui.SameLine();
|
||||||
|
using var group = ImRaii.Group();
|
||||||
|
DrawHeaderLine();
|
||||||
|
DrawPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawHeaderLine()
|
||||||
|
{
|
||||||
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameRounding, 0).Push(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||||
|
var buttonSize = new Vector2(ImGui.GetContentRegionAvail().X / 3f, 0);
|
||||||
|
|
||||||
|
using var _ = ImRaii.Group();
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.TabActive), Mode is PanelMode.SimpleAssignment);
|
||||||
|
if (ImGui.Button("Simple Assignments", buttonSize))
|
||||||
|
Mode = PanelMode.SimpleAssignment;
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
color.Pop();
|
||||||
|
color.Push(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.TabActive), Mode is PanelMode.Details);
|
||||||
|
if (ImGui.Button("Collection Details", buttonSize))
|
||||||
|
Mode = PanelMode.Details;
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
color.Pop();
|
||||||
|
color.Push(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.TabActive), Mode is PanelMode.ComplexAssignment);
|
||||||
|
if (ImGui.Button("Advanced Assignments", buttonSize))
|
||||||
|
Mode = PanelMode.ComplexAssignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawPanel()
|
||||||
|
{
|
||||||
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||||
|
using var child = ImRaii.Child("##CollectionSettings", new Vector2(-1, 0), true, ImGuiWindowFlags.HorizontalScrollbar);
|
||||||
|
if (!child)
|
||||||
|
return;
|
||||||
|
|
||||||
|
style.Pop();
|
||||||
|
switch (Mode)
|
||||||
|
{
|
||||||
|
case PanelMode.SimpleAssignment:
|
||||||
|
_tree.DrawSimple();
|
||||||
|
break;
|
||||||
|
case PanelMode.ComplexAssignment:
|
||||||
|
_tree.DrawAdvanced();
|
||||||
|
break;
|
||||||
|
case PanelMode.Details:
|
||||||
|
_panel.Draw();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
style.Push(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
299
Penumbra/UI/Tabs/CollectionsTabOld.cs
Normal file
299
Penumbra/UI/Tabs/CollectionsTabOld.cs
Normal file
|
|
@ -0,0 +1,299 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Components;
|
||||||
|
using ImGuiNET;
|
||||||
|
using OtterGui;
|
||||||
|
using OtterGui.Raii;
|
||||||
|
using OtterGui.Widgets;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.Collections.Manager;
|
||||||
|
using Penumbra.Services;
|
||||||
|
using Penumbra.UI.CollectionTab;
|
||||||
|
|
||||||
|
namespace Penumbra.UI.Tabs;
|
||||||
|
|
||||||
|
public class CollectionsTabOld : IDisposable, ITab
|
||||||
|
{
|
||||||
|
private readonly CommunicatorService _communicator;
|
||||||
|
private readonly Configuration _config;
|
||||||
|
private readonly CollectionManager _collectionManager;
|
||||||
|
private readonly TutorialService _tutorial;
|
||||||
|
private readonly SpecialCombo _specialCollectionCombo;
|
||||||
|
|
||||||
|
private readonly CollectionCombo _collectionsWithEmpty;
|
||||||
|
private readonly CollectionCombo _collectionCombo;
|
||||||
|
private readonly InheritanceUi _inheritance;
|
||||||
|
private readonly IndividualCollectionUi _individualCollections;
|
||||||
|
|
||||||
|
public CollectionsTabOld(ActorService actorService, CommunicatorService communicator, CollectionManager collectionManager,
|
||||||
|
TutorialService tutorial, Configuration config)
|
||||||
|
{
|
||||||
|
_communicator = communicator;
|
||||||
|
_collectionManager = collectionManager;
|
||||||
|
_tutorial = tutorial;
|
||||||
|
_config = config;
|
||||||
|
_specialCollectionCombo = new SpecialCombo(_collectionManager, "##NewSpecial", 350);
|
||||||
|
_collectionsWithEmpty = new CollectionCombo(_collectionManager,
|
||||||
|
() => _collectionManager.Storage.OrderBy(c => c.Name).Prepend(ModCollection.Empty).ToList());
|
||||||
|
_collectionCombo = new CollectionCombo(_collectionManager, () => _collectionManager.Storage.OrderBy(c => c.Name).ToList());
|
||||||
|
_inheritance = new InheritanceUi(_collectionManager);
|
||||||
|
_individualCollections = new IndividualCollectionUi(actorService, _collectionManager, _collectionsWithEmpty);
|
||||||
|
|
||||||
|
_communicator.CollectionChange.Subscribe(_individualCollections.UpdateIdentifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> Label
|
||||||
|
=> "Collections"u8;
|
||||||
|
|
||||||
|
/// <summary> Draw a collection selector of a certain width for a certain type. </summary>
|
||||||
|
public void DrawCollectionSelector(string label, float width, CollectionType collectionType, bool withEmpty)
|
||||||
|
=> (withEmpty ? _collectionsWithEmpty : _collectionCombo).Draw(label, width, collectionType);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
=> _communicator.CollectionChange.Unsubscribe(_individualCollections.UpdateIdentifiers);
|
||||||
|
|
||||||
|
/// <summary> Draw a tutorial step regardless of tab selection. </summary>
|
||||||
|
public void DrawHeader()
|
||||||
|
=> _tutorial.OpenTutorial(BasicTutorialSteps.Collections);
|
||||||
|
|
||||||
|
public void DrawContent()
|
||||||
|
{
|
||||||
|
using var child = ImRaii.Child("##collections", -Vector2.One);
|
||||||
|
if (child)
|
||||||
|
{
|
||||||
|
DrawActiveCollectionSelectors();
|
||||||
|
DrawMainSelectors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region New Collections
|
||||||
|
|
||||||
|
// Input text fields.
|
||||||
|
private string _newCollectionName = string.Empty;
|
||||||
|
private bool _canAddCollection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new collection that is either empty or a duplicate of the current collection.
|
||||||
|
/// Resets the new collection name.
|
||||||
|
/// </summary>
|
||||||
|
private void CreateNewCollection(bool duplicate)
|
||||||
|
{
|
||||||
|
if (_collectionManager.Storage.AddCollection(_newCollectionName, duplicate ? _collectionManager.Active.Current : null))
|
||||||
|
_newCollectionName = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Draw the Clean Unused Settings button if there are any. </summary>
|
||||||
|
private void DrawCleanCollectionButton(Vector2 width)
|
||||||
|
{
|
||||||
|
if (_collectionManager.Active.Current.UnusedSettings.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGuiUtil.DrawDisabledButton(
|
||||||
|
$"Clean {_collectionManager.Active.Current.UnusedSettings.Count} Unused Settings###CleanSettings", width
|
||||||
|
, "Remove all stored settings for mods not currently available and fix invalid settings.\n\nUse at own risk."
|
||||||
|
, false))
|
||||||
|
_collectionManager.Storage.CleanUnavailableSettings(_collectionManager.Active.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Draw the new collection input as well as its buttons. </summary>
|
||||||
|
private void DrawNewCollectionInput(Vector2 width)
|
||||||
|
{
|
||||||
|
// Input for new collection name. Also checks for validity when changed.
|
||||||
|
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X);
|
||||||
|
if (ImGui.InputTextWithHint("##New Collection", "New Collection Name...", ref _newCollectionName, 64))
|
||||||
|
_canAddCollection = _collectionManager.Storage.CanAddCollection(_newCollectionName, out _);
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiComponents.HelpMarker(
|
||||||
|
"A collection is a set of settings for your installed mods, including their enabled status, their priorities and their mod-specific configuration.\n"
|
||||||
|
+ "You can use multiple collections to quickly switch between sets of enabled mods.");
|
||||||
|
|
||||||
|
// Creation buttons.
|
||||||
|
var tt = _canAddCollection
|
||||||
|
? string.Empty
|
||||||
|
: "Please enter a unique name only consisting of symbols valid in a path but no '|' before creating a collection.";
|
||||||
|
if (ImGuiUtil.DrawDisabledButton("Create Empty Collection", width, tt, !_canAddCollection))
|
||||||
|
CreateNewCollection(false);
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGuiUtil.DrawDisabledButton($"Duplicate {TutorialService.SelectedCollection}", width, tt, !_canAddCollection))
|
||||||
|
CreateNewCollection(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Collection Selection
|
||||||
|
|
||||||
|
/// <summary> Draw all collection assignment selections. </summary>
|
||||||
|
private void DrawActiveCollectionSelectors()
|
||||||
|
{
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
var open = ImGui.CollapsingHeader(TutorialService.ActiveCollections, ImGuiTreeNodeFlags.DefaultOpen);
|
||||||
|
_tutorial.OpenTutorial(BasicTutorialSteps.ActiveCollections);
|
||||||
|
if (!open)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
||||||
|
DrawDefaultCollectionSelector();
|
||||||
|
_tutorial.OpenTutorial(BasicTutorialSteps.DefaultCollection);
|
||||||
|
DrawInterfaceCollectionSelector();
|
||||||
|
_tutorial.OpenTutorial(BasicTutorialSteps.InterfaceCollection);
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
||||||
|
DrawSpecialAssignments();
|
||||||
|
_tutorial.OpenTutorial(BasicTutorialSteps.SpecialCollections1);
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
||||||
|
_individualCollections.Draw();
|
||||||
|
_tutorial.OpenTutorial(BasicTutorialSteps.SpecialCollections2);
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawCurrentCollectionSelector(Vector2 width)
|
||||||
|
{
|
||||||
|
using var group = ImRaii.Group();
|
||||||
|
DrawCollectionSelector("##current", UiHelpers.InputTextWidth.X, CollectionType.Current, false);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiUtil.LabeledHelpMarker(TutorialService.SelectedCollection,
|
||||||
|
"This collection will be modified when using the Installed Mods tab and making changes.\nIt is not automatically assigned to anything.");
|
||||||
|
|
||||||
|
// Deletion conditions.
|
||||||
|
var deleteCondition = _collectionManager.Active.Current.Name != ModCollection.DefaultCollectionName;
|
||||||
|
var modifierHeld = Penumbra.Config.DeleteModModifier.IsActive();
|
||||||
|
var tt = deleteCondition
|
||||||
|
? modifierHeld ? string.Empty : $"Hold {_config.DeleteModModifier} while clicking to delete the collection."
|
||||||
|
: $"You can not delete the collection {ModCollection.DefaultCollectionName}.";
|
||||||
|
|
||||||
|
if (ImGuiUtil.DrawDisabledButton($"Delete {TutorialService.SelectedCollection}", width, tt, !deleteCondition || !modifierHeld))
|
||||||
|
_collectionManager.Storage.RemoveCollection(_collectionManager.Active.Current);
|
||||||
|
|
||||||
|
DrawCleanCollectionButton(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Draw the selector for the default collection assignment. </summary>
|
||||||
|
private void DrawDefaultCollectionSelector()
|
||||||
|
{
|
||||||
|
using var group = ImRaii.Group();
|
||||||
|
DrawCollectionSelector("##default", UiHelpers.InputTextWidth.X, CollectionType.Default, true);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiUtil.LabeledHelpMarker(TutorialService.DefaultCollection,
|
||||||
|
$"Mods in the {TutorialService.DefaultCollection} are loaded for anything that is not associated with the user interface or a character in the game,"
|
||||||
|
+ "as well as any character for whom no more specific conditions from below apply.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Draw the selector for the interface collection assignment. </summary>
|
||||||
|
private void DrawInterfaceCollectionSelector()
|
||||||
|
{
|
||||||
|
using var group = ImRaii.Group();
|
||||||
|
DrawCollectionSelector("##interface", UiHelpers.InputTextWidth.X, CollectionType.Interface, true);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiUtil.LabeledHelpMarker(TutorialService.InterfaceCollection,
|
||||||
|
$"Mods in the {TutorialService.InterfaceCollection} are loaded for any file that the game categorizes as an UI file. This is mostly icons as well as the tiles that generate the user interface windows themselves.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Description for character groups used in multiple help markers. </summary>
|
||||||
|
private const string CharacterGroupDescription =
|
||||||
|
$"{TutorialService.CharacterGroups} apply to certain types of characters based on a condition.\n"
|
||||||
|
+ $"All of them take precedence before the {TutorialService.DefaultCollection},\n"
|
||||||
|
+ $"but all {TutorialService.IndividualAssignments} take precedence before them.";
|
||||||
|
|
||||||
|
/// <summary> Draw the entire group assignment section. </summary>
|
||||||
|
private void DrawSpecialAssignments()
|
||||||
|
{
|
||||||
|
using var _ = ImRaii.Group();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted(TutorialService.CharacterGroups);
|
||||||
|
ImGuiComponents.HelpMarker(CharacterGroupDescription);
|
||||||
|
ImGui.Separator();
|
||||||
|
DrawSpecialCollections();
|
||||||
|
ImGui.Dummy(Vector2.Zero);
|
||||||
|
DrawNewSpecialCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Draw a new combo to select special collections as well as button to create it. </summary>
|
||||||
|
private void DrawNewSpecialCollection()
|
||||||
|
{
|
||||||
|
ImGui.SetNextItemWidth(UiHelpers.InputTextWidth.X);
|
||||||
|
if (_specialCollectionCombo.CurrentIdx == -1
|
||||||
|
|| _collectionManager.Active.ByType(_specialCollectionCombo.CurrentType!.Value.Item1) != null)
|
||||||
|
{
|
||||||
|
_specialCollectionCombo.ResetFilter();
|
||||||
|
_specialCollectionCombo.CurrentIdx = CollectionTypeExtensions.Special
|
||||||
|
.IndexOf(t => _collectionManager.Active.ByType(t.Item1) == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_specialCollectionCombo.CurrentType == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_specialCollectionCombo.Draw();
|
||||||
|
ImGui.SameLine();
|
||||||
|
var disabled = _specialCollectionCombo.CurrentType == null;
|
||||||
|
var tt = disabled
|
||||||
|
? $"Please select a condition for a {TutorialService.GroupAssignment} before creating the collection.\n\n"
|
||||||
|
+ CharacterGroupDescription
|
||||||
|
: CharacterGroupDescription;
|
||||||
|
if (!ImGuiUtil.DrawDisabledButton($"Assign {TutorialService.ConditionalGroup}", new Vector2(120 * UiHelpers.Scale, 0), tt, disabled))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_collectionManager.Active.CreateSpecialCollection(_specialCollectionCombo.CurrentType!.Value.Item1);
|
||||||
|
_specialCollectionCombo.CurrentIdx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Current Collection Editing
|
||||||
|
|
||||||
|
/// <summary> Draw the current collection selection, the creation of new collections and the inheritance block. </summary>
|
||||||
|
private void DrawMainSelectors()
|
||||||
|
{
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
var open = ImGui.CollapsingHeader("Collection Settings", ImGuiTreeNodeFlags.DefaultOpen);
|
||||||
|
_tutorial.OpenTutorial(BasicTutorialSteps.EditingCollections);
|
||||||
|
if (!open)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var width = new Vector2((UiHelpers.InputTextWidth.X - ImGui.GetStyle().ItemSpacing.X) / 2, 0);
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
||||||
|
DrawCurrentCollectionSelector(width);
|
||||||
|
_tutorial.OpenTutorial(BasicTutorialSteps.CurrentCollection);
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
||||||
|
DrawNewCollectionInput(width);
|
||||||
|
UiHelpers.DefaultLineSpace();
|
||||||
|
|
||||||
|
_inheritance.Draw();
|
||||||
|
_tutorial.OpenTutorial(BasicTutorialSteps.Inheritance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Draw all currently set special collections. </summary>
|
||||||
|
private void DrawSpecialCollections()
|
||||||
|
{
|
||||||
|
foreach (var (type, name, desc) in CollectionTypeExtensions.Special)
|
||||||
|
{
|
||||||
|
var collection = _collectionManager.Active.ByType(type);
|
||||||
|
if (collection == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
using var id = ImRaii.PushId((int)type);
|
||||||
|
DrawCollectionSelector("##SpecialCombo", UiHelpers.InputTextWidth.X, type, true);
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), UiHelpers.IconButtonSize, string.Empty,
|
||||||
|
false, true))
|
||||||
|
{
|
||||||
|
_collectionManager.Active.RemoveSpecialCollection(type);
|
||||||
|
_specialCollectionCombo.ResetFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGuiUtil.LabeledHelpMarker(name, desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
@ -9,15 +9,15 @@ using System.Numerics;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using OtterGui.Widgets;
|
using OtterGui.Widgets;
|
||||||
using Penumbra.Api.Enums;
|
using Penumbra.Api.Enums;
|
||||||
using Penumbra.Interop;
|
|
||||||
using Penumbra.Interop.Services;
|
using Penumbra.Interop.Services;
|
||||||
using Penumbra.Mods;
|
using Penumbra.Mods;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
using Penumbra.Services;
|
using Penumbra.Services;
|
||||||
using Penumbra.UI.ModsTab;
|
using Penumbra.UI.ModsTab;
|
||||||
using ModFileSystemSelector = Penumbra.UI.ModsTab.ModFileSystemSelector;
|
using ModFileSystemSelector = Penumbra.UI.ModsTab.ModFileSystemSelector;
|
||||||
using Penumbra.Collections.Manager;
|
using Penumbra.Collections.Manager;
|
||||||
|
using Penumbra.UI.CollectionTab;
|
||||||
|
|
||||||
namespace Penumbra.UI.Tabs;
|
namespace Penumbra.UI.Tabs;
|
||||||
|
|
||||||
public class ModsTab : ITab
|
public class ModsTab : ITab
|
||||||
|
|
@ -25,23 +25,23 @@ public class ModsTab : ITab
|
||||||
private readonly ModFileSystemSelector _selector;
|
private readonly ModFileSystemSelector _selector;
|
||||||
private readonly ModPanel _panel;
|
private readonly ModPanel _panel;
|
||||||
private readonly TutorialService _tutorial;
|
private readonly TutorialService _tutorial;
|
||||||
private readonly ModManager _modManager;
|
private readonly ModManager _modManager;
|
||||||
private readonly CollectionManager _collectionManager;
|
private readonly ActiveCollections _activeCollections;
|
||||||
private readonly RedrawService _redrawService;
|
private readonly RedrawService _redrawService;
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
private readonly CollectionsTab _collectionsTab;
|
private readonly CollectionCombo _collectionCombo;
|
||||||
|
|
||||||
public ModsTab(ModManager modManager, CollectionManager collectionManager, ModFileSystemSelector selector, ModPanel panel,
|
public ModsTab(ModManager modManager, CollectionManager collectionManager, ModFileSystemSelector selector, ModPanel panel,
|
||||||
TutorialService tutorial, RedrawService redrawService, Configuration config, CollectionsTab collectionsTab)
|
TutorialService tutorial, RedrawService redrawService, Configuration config)
|
||||||
{
|
{
|
||||||
_modManager = modManager;
|
_modManager = modManager;
|
||||||
_collectionManager = collectionManager;
|
_activeCollections = collectionManager.Active;
|
||||||
_selector = selector;
|
_selector = selector;
|
||||||
_panel = panel;
|
_panel = panel;
|
||||||
_tutorial = tutorial;
|
_tutorial = tutorial;
|
||||||
_redrawService = redrawService;
|
_redrawService = redrawService;
|
||||||
_config = config;
|
_config = config;
|
||||||
_collectionsTab = collectionsTab;
|
_collectionCombo = new CollectionCombo(collectionManager, () => collectionManager.Storage.OrderBy(c => c.Name).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsVisible
|
public bool IsVisible
|
||||||
|
|
@ -62,14 +62,14 @@ public class ModsTab : ITab
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_selector.Draw(GetModSelectorSize());
|
_selector.Draw(GetModSelectorSize(_config));
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using var group = ImRaii.Group();
|
using var group = ImRaii.Group();
|
||||||
DrawHeaderLine();
|
DrawHeaderLine();
|
||||||
|
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||||
|
|
||||||
using (var child = ImRaii.Child("##ModsTabMod", new Vector2(-1, Penumbra.Config.HideRedrawBar ? 0 : -ImGui.GetFrameHeight()),
|
using (var child = ImRaii.Child("##ModsTabMod", new Vector2(-1, _config.HideRedrawBar ? 0 : -ImGui.GetFrameHeight()),
|
||||||
true, ImGuiWindowFlags.HorizontalScrollbar))
|
true, ImGuiWindowFlags.HorizontalScrollbar))
|
||||||
{
|
{
|
||||||
style.Pop();
|
style.Pop();
|
||||||
|
|
@ -86,16 +86,29 @@ public class ModsTab : ITab
|
||||||
{
|
{
|
||||||
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"
|
||||||
+ $"{_collectionManager.Active.Current.AnonymizedName} Current Collection\n"
|
+ $"{_activeCollections.Current.AnonymizedName} Current Collection\n"
|
||||||
+ $"{_collectionManager.Active.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(", ", _collectionManager.Active.Current.DirectlyInheritsFrom.Select(c => c.AnonymizedName))} Inheritances\n"
|
+ $"{string.Join(", ", _activeCollections.Current.DirectlyInheritsFrom.Select(c => c.AnonymizedName))} Inheritances\n"
|
||||||
+ $"{_selector.SelectedSettingCollection.AnonymizedName} Collection\n");
|
+ $"{_selector.SelectedSettingCollection.AnonymizedName} Collection\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Get the correct size for the mod selector based on current config. </summary>
|
||||||
|
public static float GetModSelectorSize(Configuration config)
|
||||||
|
{
|
||||||
|
var absoluteSize = Math.Clamp(config.ModSelectorAbsoluteSize, Configuration.Constants.MinAbsoluteSize,
|
||||||
|
Math.Min(Configuration.Constants.MaxAbsoluteSize, ImGui.GetContentRegionAvail().X - 100));
|
||||||
|
var relativeSize = config.ScaleModSelector
|
||||||
|
? Math.Clamp(config.ModSelectorScaledSize, Configuration.Constants.MinScaledSize, Configuration.Constants.MaxScaledSize)
|
||||||
|
: 0;
|
||||||
|
return !config.ScaleModSelector
|
||||||
|
? absoluteSize
|
||||||
|
: Math.Max(absoluteSize, relativeSize * ImGui.GetContentRegionAvail().X / 100);
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawRedrawLine()
|
private void DrawRedrawLine()
|
||||||
{
|
{
|
||||||
if (Penumbra.Config.HideRedrawBar)
|
if (Penumbra.Config.HideRedrawBar)
|
||||||
|
|
@ -159,32 +172,32 @@ public class ModsTab : ITab
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
DrawInheritedCollectionButton(3 * buttonSize);
|
DrawInheritedCollectionButton(3 * buttonSize);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
_collectionsTab.DrawCollectionSelector("##collectionSelector", 2 * buttonSize.X, CollectionType.Current, false);
|
_collectionCombo.Draw("##collectionSelector", 2 * buttonSize.X, CollectionType.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
_tutorial.OpenTutorial(BasicTutorialSteps.CollectionSelectors);
|
_tutorial.OpenTutorial(BasicTutorialSteps.CollectionSelectors);
|
||||||
|
|
||||||
if (!_collectionManager.Active.CurrentCollectionInUse)
|
if (!_activeCollections.CurrentCollectionInUse)
|
||||||
ImGuiUtil.DrawTextButton("The currently selected collection is not used in any way.", -Vector2.UnitX, Colors.PressEnterWarningBg);
|
ImGuiUtil.DrawTextButton("The currently selected collection is not used in any way.", -Vector2.UnitX, Colors.PressEnterWarningBg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawDefaultCollectionButton(Vector2 width)
|
private void DrawDefaultCollectionButton(Vector2 width)
|
||||||
{
|
{
|
||||||
var name = $"{TutorialService.DefaultCollection} ({_collectionManager.Active.Default.Name})";
|
var name = $"{TutorialService.DefaultCollection} ({_activeCollections.Default.Name})";
|
||||||
var isCurrent = _collectionManager.Active.Default == _collectionManager.Active.Current;
|
var isCurrent = _activeCollections.Default == _activeCollections.Current;
|
||||||
var isEmpty = _collectionManager.Active.Default == ModCollection.Empty;
|
var isEmpty = _activeCollections.Default == ModCollection.Empty;
|
||||||
var tt = isCurrent ? $"The current collection is already the configured {TutorialService.DefaultCollection}."
|
var tt = isCurrent ? $"The current collection is already the configured {TutorialService.DefaultCollection}."
|
||||||
: isEmpty ? $"The {TutorialService.DefaultCollection} is configured to be empty."
|
: isEmpty ? $"The {TutorialService.DefaultCollection} is configured to be empty."
|
||||||
: $"Set the {TutorialService.SelectedCollection} to the configured {TutorialService.DefaultCollection}.";
|
: $"Set the {TutorialService.SelectedCollection} to the configured {TutorialService.DefaultCollection}.";
|
||||||
if (ImGuiUtil.DrawDisabledButton(name, width, tt, isCurrent || isEmpty))
|
if (ImGuiUtil.DrawDisabledButton(name, width, tt, isCurrent || isEmpty))
|
||||||
_collectionManager.Active.SetCollection(_collectionManager.Active.Default, CollectionType.Current);
|
_activeCollections.SetCollection(_activeCollections.Default, CollectionType.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawInheritedCollectionButton(Vector2 width)
|
private void DrawInheritedCollectionButton(Vector2 width)
|
||||||
{
|
{
|
||||||
var noModSelected = _selector.Selected == null;
|
var noModSelected = _selector.Selected == null;
|
||||||
var collection = _selector.SelectedSettingCollection;
|
var collection = _selector.SelectedSettingCollection;
|
||||||
var modInherited = collection != _collectionManager.Active.Current;
|
var modInherited = collection != _activeCollections.Current;
|
||||||
var (name, tt) = (noModSelected, modInherited) switch
|
var (name, tt) = (noModSelected, modInherited) switch
|
||||||
{
|
{
|
||||||
(true, _) => ("Inherited Collection", "No mod selected."),
|
(true, _) => ("Inherited Collection", "No mod selected."),
|
||||||
|
|
@ -193,19 +206,6 @@ public class ModsTab : ITab
|
||||||
(false, false) => ("Not Inherited", "The selected mod does not inherit its settings."),
|
(false, false) => ("Not Inherited", "The selected mod does not inherit its settings."),
|
||||||
};
|
};
|
||||||
if (ImGuiUtil.DrawDisabledButton(name, width, tt, noModSelected || !modInherited))
|
if (ImGuiUtil.DrawDisabledButton(name, width, tt, noModSelected || !modInherited))
|
||||||
_collectionManager.Active.SetCollection(collection, CollectionType.Current);
|
_activeCollections.SetCollection(collection, CollectionType.Current);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Get the correct size for the mod selector based on current config. </summary>
|
|
||||||
private float GetModSelectorSize()
|
|
||||||
{
|
|
||||||
var absoluteSize = Math.Clamp(_config.ModSelectorAbsoluteSize, Configuration.Constants.MinAbsoluteSize,
|
|
||||||
Math.Min(Configuration.Constants.MaxAbsoluteSize, ImGui.GetContentRegionAvail().X - 100));
|
|
||||||
var relativeSize = _config.ScaleModSelector
|
|
||||||
? Math.Clamp(_config.ModSelectorScaledSize, Configuration.Constants.MinScaledSize, Configuration.Constants.MaxScaledSize)
|
|
||||||
: 0;
|
|
||||||
return !_config.ScaleModSelector
|
|
||||||
? absoluteSize
|
|
||||||
: Math.Max(absoluteSize, relativeSize * ImGui.GetContentRegionAvail().X / 100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue