mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add special collections.
This commit is contained in:
parent
ec91755065
commit
549f8ce4b4
11 changed files with 486 additions and 86 deletions
|
|
@ -39,19 +39,59 @@ public partial class ModCollection
|
|||
public ModCollection Character( string name )
|
||||
=> _characters.TryGetValue( name, out var c ) ? c : Default;
|
||||
|
||||
// Set a active collection, can be used to set Default, Current or Character collections.
|
||||
private void SetCollection( int newIdx, Type type, string? characterName = null )
|
||||
// Special Collections
|
||||
private readonly ModCollection?[] _specialCollections = new ModCollection?[Enum.GetValues< CollectionType >().Length - 4];
|
||||
|
||||
// Return the configured collection for the given type or null.
|
||||
public ModCollection? ByType( CollectionType type, string? name = null )
|
||||
{
|
||||
var oldCollectionIdx = type switch
|
||||
if( type.IsSpecial() )
|
||||
{
|
||||
Type.Default => Default.Index,
|
||||
Type.Current => Current.Index,
|
||||
Type.Character => characterName?.Length > 0
|
||||
return _specialCollections[ ( int )type ];
|
||||
}
|
||||
|
||||
return type switch
|
||||
{
|
||||
CollectionType.Default => Default,
|
||||
CollectionType.Current => Current,
|
||||
CollectionType.Character => name != null ? _characters.TryGetValue( name, out var c ) ? c : null : null,
|
||||
CollectionType.Inactive => name != null ? ByName( name, out var c ) ? c : null : null,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
// Set a active collection, can be used to set Default, Current or Character collections.
|
||||
private void SetCollection( int newIdx, CollectionType collectionType, string? characterName = null )
|
||||
{
|
||||
var oldCollectionIdx = collectionType switch
|
||||
{
|
||||
CollectionType.Default => Default.Index,
|
||||
CollectionType.Current => Current.Index,
|
||||
CollectionType.Character => characterName?.Length > 0
|
||||
? _characters.TryGetValue( characterName, out var c )
|
||||
? c.Index
|
||||
: Default.Index
|
||||
: -1,
|
||||
_ => -1,
|
||||
CollectionType.Yourself => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.PlayerCharacter => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.NonPlayerCharacter => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Midlander => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Highlander => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Wildwood => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Duskwight => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Plainsfolk => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Dunesfolk => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.SeekerOfTheSun => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.KeeperOfTheMoon => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Seawolf => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Hellsguard => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Raen => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Xaela => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Helion => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Lost => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Rava => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
CollectionType.Veena => _specialCollections[ ( int )collectionType ]?.Index ?? Default.Index,
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
if( oldCollectionIdx == -1 || newIdx == oldCollectionIdx )
|
||||
|
|
@ -66,31 +106,109 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
RemoveCache( oldCollectionIdx );
|
||||
switch( type )
|
||||
switch( collectionType )
|
||||
{
|
||||
case Type.Default:
|
||||
case CollectionType.Default:
|
||||
Default = newCollection;
|
||||
Penumbra.ResidentResources.Reload();
|
||||
Default.SetFiles();
|
||||
break;
|
||||
case Type.Current:
|
||||
case CollectionType.Current:
|
||||
Current = newCollection;
|
||||
break;
|
||||
case Type.Character:
|
||||
case CollectionType.Character:
|
||||
_characters[ characterName! ] = newCollection;
|
||||
break;
|
||||
default:
|
||||
_specialCollections[ ( int )collectionType ] = newCollection;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
UpdateCurrentCollectionInUse();
|
||||
CollectionChanged.Invoke( type, this[ oldCollectionIdx ], newCollection, characterName );
|
||||
CollectionChanged.Invoke( collectionType, this[ oldCollectionIdx ], newCollection, characterName );
|
||||
}
|
||||
|
||||
private void UpdateCurrentCollectionInUse()
|
||||
=> CurrentCollectionInUse = Characters.Values.Prepend( Default ).SelectMany( c => c.GetFlattenedInheritance() ).Contains( Current );
|
||||
=> CurrentCollectionInUse = _specialCollections
|
||||
.OfType< ModCollection >()
|
||||
.Prepend( Default )
|
||||
.Concat( Characters.Values )
|
||||
.SelectMany( c => c.GetFlattenedInheritance() ).Contains( Current );
|
||||
|
||||
public void SetCollection( ModCollection collection, Type type, string? characterName = null )
|
||||
=> SetCollection( collection.Index, type, characterName );
|
||||
public void SetCollection( ModCollection collection, CollectionType collectionType, string? characterName = null )
|
||||
=> SetCollection( collection.Index, collectionType, characterName );
|
||||
|
||||
// Create a special collection if it does not exist and set it to Empty.
|
||||
public bool CreateSpecialCollection( CollectionType collectionType )
|
||||
{
|
||||
switch( collectionType )
|
||||
{
|
||||
case CollectionType.Yourself:
|
||||
case CollectionType.PlayerCharacter:
|
||||
case CollectionType.NonPlayerCharacter:
|
||||
case CollectionType.Midlander:
|
||||
case CollectionType.Highlander:
|
||||
case CollectionType.Wildwood:
|
||||
case CollectionType.Duskwight:
|
||||
case CollectionType.Plainsfolk:
|
||||
case CollectionType.Dunesfolk:
|
||||
case CollectionType.SeekerOfTheSun:
|
||||
case CollectionType.KeeperOfTheMoon:
|
||||
case CollectionType.Seawolf:
|
||||
case CollectionType.Hellsguard:
|
||||
case CollectionType.Raen:
|
||||
case CollectionType.Xaela:
|
||||
case CollectionType.Helion:
|
||||
case CollectionType.Lost:
|
||||
case CollectionType.Rava:
|
||||
case CollectionType.Veena:
|
||||
if( _specialCollections[ ( int )collectionType ] != null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_specialCollections[ ( int )collectionType ] = Empty;
|
||||
CollectionChanged.Invoke( collectionType, null, Empty, null );
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a special collection if it exists
|
||||
public void RemoveSpecialCollection( CollectionType collectionType )
|
||||
{
|
||||
switch( collectionType )
|
||||
{
|
||||
case CollectionType.Yourself:
|
||||
case CollectionType.PlayerCharacter:
|
||||
case CollectionType.NonPlayerCharacter:
|
||||
case CollectionType.Midlander:
|
||||
case CollectionType.Highlander:
|
||||
case CollectionType.Wildwood:
|
||||
case CollectionType.Duskwight:
|
||||
case CollectionType.Plainsfolk:
|
||||
case CollectionType.Dunesfolk:
|
||||
case CollectionType.SeekerOfTheSun:
|
||||
case CollectionType.KeeperOfTheMoon:
|
||||
case CollectionType.Seawolf:
|
||||
case CollectionType.Hellsguard:
|
||||
case CollectionType.Raen:
|
||||
case CollectionType.Xaela:
|
||||
case CollectionType.Helion:
|
||||
case CollectionType.Lost:
|
||||
case CollectionType.Rava:
|
||||
case CollectionType.Veena:
|
||||
var old = _specialCollections[ ( int )collectionType ];
|
||||
if( old != null )
|
||||
{
|
||||
_specialCollections[ ( int )collectionType ] = null;
|
||||
CollectionChanged.Invoke( collectionType, old, null, null );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new character collection. Returns false if the character name already has a collection.
|
||||
public bool CreateCharacterCollection( string characterName )
|
||||
|
|
@ -101,7 +219,7 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
_characters[ characterName ] = Empty;
|
||||
CollectionChanged.Invoke( Type.Character, null, Empty, characterName );
|
||||
CollectionChanged.Invoke( CollectionType.Character, null, Empty, characterName );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +230,7 @@ public partial class ModCollection
|
|||
{
|
||||
RemoveCache( collection.Index );
|
||||
_characters.Remove( characterName );
|
||||
CollectionChanged.Invoke( Type.Character, collection, null, characterName );
|
||||
CollectionChanged.Invoke( CollectionType.Character, collection, null, characterName );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +275,25 @@ public partial class ModCollection
|
|||
Current = this[ currentIdx ];
|
||||
}
|
||||
|
||||
// Load special collections.
|
||||
foreach( var type in CollectionTypeExtensions.Special )
|
||||
{
|
||||
var typeName = jObject[ type.ToString() ]?.ToObject< string >();
|
||||
if( typeName != null )
|
||||
{
|
||||
var idx = GetIndexForCollectionName( typeName );
|
||||
if( idx < 0 )
|
||||
{
|
||||
PluginLog.Error( $"Last choice of {type.ToName()} Collection {typeName} is not available, removed." );
|
||||
configChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_specialCollections[ ( int )type ] = this[ idx ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load character collections. If a player name comes up multiple times, the last one is applied.
|
||||
var characters = jObject[ nameof( Characters ) ]?.ToObject< Dictionary< string, string > >() ?? new Dictionary< string, string >();
|
||||
foreach( var (player, collectionName) in characters )
|
||||
|
|
@ -187,10 +324,14 @@ public partial class ModCollection
|
|||
public void SaveActiveCollections()
|
||||
{
|
||||
Penumbra.Framework.RegisterDelayed( nameof( SaveActiveCollections ),
|
||||
() => SaveActiveCollections( Default.Name, Current.Name, Characters.Select( kvp => ( kvp.Key, kvp.Value.Name ) ) ) );
|
||||
() => SaveActiveCollections( Default.Name, Current.Name, Characters.Select( kvp => ( kvp.Key, kvp.Value.Name ) ),
|
||||
_specialCollections.WithIndex()
|
||||
.Where( c => c.Item1 != null )
|
||||
.Select( c => ( ( CollectionType )c.Item2, c.Item1!.Name ) ) ) );
|
||||
}
|
||||
|
||||
internal static void SaveActiveCollections( string def, string current, IEnumerable< (string, string) > characters )
|
||||
internal static void SaveActiveCollections( string def, string current, IEnumerable< (string, string) > characters,
|
||||
IEnumerable< (CollectionType, string) > special )
|
||||
{
|
||||
var file = ActiveCollectionFile;
|
||||
try
|
||||
|
|
@ -204,6 +345,12 @@ public partial class ModCollection
|
|||
j.WriteValue( def );
|
||||
j.WritePropertyName( nameof( Current ) );
|
||||
j.WriteValue( current );
|
||||
foreach( var (type, collection) in special )
|
||||
{
|
||||
j.WritePropertyName( type.ToString() );
|
||||
j.WriteValue( collection );
|
||||
}
|
||||
|
||||
j.WritePropertyName( nameof( Characters ) );
|
||||
j.WriteStartObject();
|
||||
foreach( var (character, collection) in characters )
|
||||
|
|
@ -246,9 +393,9 @@ public partial class ModCollection
|
|||
|
||||
|
||||
// Save if any of the active collections is changed.
|
||||
private void SaveOnChange( Type type, ModCollection? _1, ModCollection? _2, string? _3 )
|
||||
private void SaveOnChange( CollectionType collectionType, ModCollection? _1, ModCollection? _2, string? _3 )
|
||||
{
|
||||
if( type != Type.Inactive )
|
||||
if( collectionType != CollectionType.Inactive )
|
||||
{
|
||||
SaveActiveCollections();
|
||||
}
|
||||
|
|
@ -261,7 +408,7 @@ public partial class ModCollection
|
|||
Default.CreateCache();
|
||||
Current.CreateCache();
|
||||
|
||||
foreach( var collection in _characters.Values )
|
||||
foreach( var collection in _specialCollections.OfType< ModCollection >().Concat( _characters.Values ) )
|
||||
{
|
||||
collection.CreateCache();
|
||||
}
|
||||
|
|
@ -269,7 +416,10 @@ public partial class ModCollection
|
|||
|
||||
private void RemoveCache( int idx )
|
||||
{
|
||||
if( idx != Default.Index && idx != Current.Index && _characters.Values.All( c => c.Index != idx ) )
|
||||
if( idx != Default.Index
|
||||
&& idx != Current.Index
|
||||
&& _specialCollections.All( c => c == null || c.Index != idx )
|
||||
&& _characters.Values.All( c => c.Index != idx ) )
|
||||
{
|
||||
_collections[ idx ].ClearCache();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,12 @@ namespace Penumbra.Collections;
|
|||
|
||||
public partial class ModCollection
|
||||
{
|
||||
public enum Type : byte
|
||||
{
|
||||
Inactive, // A collection was added or removed
|
||||
Default, // The default collection was changed
|
||||
Character, // A character collection was changed
|
||||
Current, // The current collection was changed.
|
||||
}
|
||||
|
||||
public sealed partial class Manager : IDisposable, IEnumerable< ModCollection >
|
||||
{
|
||||
// On addition, oldCollection is null. On deletion, newCollection is null.
|
||||
// CharacterName is onls set for type == Character.
|
||||
public delegate void CollectionChangeDelegate( Type type, ModCollection? oldCollection, ModCollection? newCollection,
|
||||
string? characterName = null );
|
||||
// CharacterName is only set for type == Character.
|
||||
public delegate void CollectionChangeDelegate( CollectionType collectionType, ModCollection? oldCollection,
|
||||
ModCollection? newCollection, string? characterName = null );
|
||||
|
||||
private readonly Mod.Manager _modManager;
|
||||
|
||||
|
|
@ -124,8 +116,8 @@ public partial class ModCollection
|
|||
_collections.Add( newCollection );
|
||||
newCollection.Save();
|
||||
PluginLog.Debug( "Added collection {Name:l}.", newCollection.Name );
|
||||
CollectionChanged.Invoke( Type.Inactive, null, newCollection );
|
||||
SetCollection( newCollection.Index, Type.Current );
|
||||
CollectionChanged.Invoke( CollectionType.Inactive, null, newCollection );
|
||||
SetCollection( newCollection.Index, CollectionType.Current );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -148,17 +140,17 @@ public partial class ModCollection
|
|||
|
||||
if( idx == Current.Index )
|
||||
{
|
||||
SetCollection( DefaultName, Type.Current );
|
||||
SetCollection( DefaultName, CollectionType.Current );
|
||||
}
|
||||
|
||||
if( idx == Default.Index )
|
||||
{
|
||||
SetCollection( Empty, Type.Default );
|
||||
SetCollection( Empty, CollectionType.Default );
|
||||
}
|
||||
|
||||
foreach( var (characterName, _) in _characters.Where( c => c.Value.Index == idx ).ToList() )
|
||||
{
|
||||
SetCollection( Empty, Type.Character, characterName );
|
||||
SetCollection( Empty, CollectionType.Character, characterName );
|
||||
}
|
||||
|
||||
var collection = _collections[ idx ];
|
||||
|
|
@ -179,7 +171,7 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
PluginLog.Debug( "Removed collection {Name:l}.", collection.Name );
|
||||
CollectionChanged.Invoke( Type.Inactive, collection, null );
|
||||
CollectionChanged.Invoke( CollectionType.Inactive, collection, null );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
115
Penumbra/Collections/CollectionType.cs
Normal file
115
Penumbra/Collections/CollectionType.cs
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Penumbra.Collections;
|
||||
|
||||
public enum CollectionType : byte
|
||||
{
|
||||
// Special Collections
|
||||
Yourself = 0,
|
||||
PlayerCharacter,
|
||||
NonPlayerCharacter,
|
||||
Midlander,
|
||||
Highlander,
|
||||
Wildwood,
|
||||
Duskwight,
|
||||
Plainsfolk,
|
||||
Dunesfolk,
|
||||
SeekerOfTheSun,
|
||||
KeeperOfTheMoon,
|
||||
Seawolf,
|
||||
Hellsguard,
|
||||
Raen,
|
||||
Xaela,
|
||||
Helion,
|
||||
Lost,
|
||||
Rava,
|
||||
Veena,
|
||||
|
||||
Inactive, // A collection was added or removed
|
||||
Default, // The default collection was changed
|
||||
Character, // A character collection was changed
|
||||
Current, // The current collection was changed.
|
||||
}
|
||||
|
||||
public static class CollectionTypeExtensions
|
||||
{
|
||||
public static bool IsSpecial( this CollectionType collectionType )
|
||||
=> collectionType is >= CollectionType.Yourself and < CollectionType.Inactive;
|
||||
|
||||
public static readonly CollectionType[] Special = Enum.GetValues< CollectionType >().Where( IsSpecial ).ToArray();
|
||||
|
||||
public static string ToName( this CollectionType collectionType )
|
||||
=> collectionType switch
|
||||
{
|
||||
CollectionType.Yourself => "Your Character",
|
||||
CollectionType.PlayerCharacter => "Player Characters",
|
||||
CollectionType.NonPlayerCharacter => "Non-Player Characters",
|
||||
CollectionType.Midlander => SubRace.Midlander.ToName(),
|
||||
CollectionType.Highlander => SubRace.Highlander.ToName(),
|
||||
CollectionType.Wildwood => SubRace.Wildwood.ToName(),
|
||||
CollectionType.Duskwight => SubRace.Duskwight.ToName(),
|
||||
CollectionType.Plainsfolk => SubRace.Plainsfolk.ToName(),
|
||||
CollectionType.Dunesfolk => SubRace.Dunesfolk.ToName(),
|
||||
CollectionType.SeekerOfTheSun => SubRace.SeekerOfTheSun.ToName(),
|
||||
CollectionType.KeeperOfTheMoon => SubRace.KeeperOfTheMoon.ToName(),
|
||||
CollectionType.Seawolf => SubRace.Seawolf.ToName(),
|
||||
CollectionType.Hellsguard => SubRace.Hellsguard.ToName(),
|
||||
CollectionType.Raen => SubRace.Raen.ToName(),
|
||||
CollectionType.Xaela => SubRace.Xaela.ToName(),
|
||||
CollectionType.Helion => SubRace.Helion.ToName(),
|
||||
CollectionType.Lost => SubRace.Lost.ToName(),
|
||||
CollectionType.Rava => SubRace.Rava.ToName(),
|
||||
CollectionType.Veena => SubRace.Veena.ToName(),
|
||||
CollectionType.Inactive => "Collection",
|
||||
CollectionType.Default => "Default",
|
||||
CollectionType.Character => "Character",
|
||||
CollectionType.Current => "Current",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
public static string ToDescription( this CollectionType collectionType )
|
||||
=> collectionType switch
|
||||
{
|
||||
CollectionType.Yourself => "This collection applies to your own character, regardless of its name.\n"
|
||||
+ "It takes precedence before all other collections except for explicitly named character collections.",
|
||||
CollectionType.PlayerCharacter =>
|
||||
"This collection applies to all player characters that do not have a more specific character or racial collections associated.",
|
||||
CollectionType.NonPlayerCharacter =>
|
||||
"This collection applies to all human non-player characters except those explicitly named. It takes precedence before the default and racial collections.",
|
||||
CollectionType.Midlander =>
|
||||
"This collection applies to all player character Midlander Hyur that do not have a more specific character collection associated.",
|
||||
CollectionType.Highlander =>
|
||||
"This collection applies to all player character Highlander Hyur that do not have a more specific character collection associated.",
|
||||
CollectionType.Wildwood =>
|
||||
"This collection applies to all player character Wildwood Elezen that do not have a more specific character collection associated.",
|
||||
CollectionType.Duskwight =>
|
||||
"This collection applies to all player character Duskwight Elezen that do not have a more specific character collection associated.",
|
||||
CollectionType.Plainsfolk =>
|
||||
"This collection applies to all player character Plainsfolk Lalafell that do not have a more specific character collection associated.",
|
||||
CollectionType.Dunesfolk =>
|
||||
"This collection applies to all player character Dunesfolk Lalafell that do not have a more specific character collection associated.",
|
||||
CollectionType.SeekerOfTheSun =>
|
||||
"This collection applies to all player character Seekers of the Sun that do not have a more specific character collection associated.",
|
||||
CollectionType.KeeperOfTheMoon =>
|
||||
"This collection applies to all player character Keepers of the Moon that do not have a more specific character collection associated.",
|
||||
CollectionType.Seawolf =>
|
||||
"This collection applies to all player character Sea Wolf Roegadyn that do not have a more specific character collection associated.",
|
||||
CollectionType.Hellsguard =>
|
||||
"This collection applies to all player character Hellsguard Roegadyn that do not have a more specific character collection associated.",
|
||||
CollectionType.Raen =>
|
||||
"This collection applies to all player character Raen Au Ra that do not have a more specific character collection associated.",
|
||||
CollectionType.Xaela =>
|
||||
"This collection applies to all player character Xaela Au Ra that do not have a more specific character collection associated.",
|
||||
CollectionType.Helion =>
|
||||
"This collection applies to all player character Helion Hrothgar that do not have a more specific character collection associated.",
|
||||
CollectionType.Lost =>
|
||||
"This collection applies to all player character Lost Hrothgar that do not have a more specific character collection associated.",
|
||||
CollectionType.Rava =>
|
||||
"This collection applies to all player character Rava Viera that do not have a more specific character collection associated.",
|
||||
CollectionType.Veena =>
|
||||
"This collection applies to all player character Veena Viera that do not have a more specific character collection associated.",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
|
@ -182,7 +182,7 @@ public partial class Configuration
|
|||
DefaultCollection = _data[ nameof( DefaultCollection ) ]?.ToObject< string >() ?? DefaultCollection;
|
||||
CharacterCollections = _data[ nameof( CharacterCollections ) ]?.ToObject< Dictionary< string, string > >() ?? CharacterCollections;
|
||||
ModCollection.Manager.SaveActiveCollections( DefaultCollection, CurrentCollection,
|
||||
CharacterCollections.Select( kvp => ( kvp.Key, kvp.Value ) ) );
|
||||
CharacterCollections.Select( kvp => ( kvp.Key, kvp.Value ) ), Array.Empty<(CollectionType, string)>() );
|
||||
}
|
||||
|
||||
// Collections were introduced and the previous CurrentCollection got put into ModDirectory.
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using FFXIVClientStructs.FFXIV.Client.UI;
|
|||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||
|
||||
namespace Penumbra.Interop.Resolver;
|
||||
|
|
@ -320,8 +321,12 @@ public unsafe partial class PathResolver
|
|||
}
|
||||
?? GetOwnerName( gameObject ) ?? actorName ?? new Utf8String( gameObject->Name ).ToString();
|
||||
|
||||
// First check temporary character collections, then the own configuration.
|
||||
return CollectionByActorName( actualName, out var c ) ? c : Penumbra.CollectionManager.Default;
|
||||
// First check temporary character collections, then the own configuration, then special collections.
|
||||
return CollectionByActorName( actualName, out var c )
|
||||
? c
|
||||
: CollectionByActor( actualName, gameObject, out c )
|
||||
? c
|
||||
: Penumbra.CollectionManager.Default;
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
|
|
@ -335,11 +340,75 @@ public unsafe partial class PathResolver
|
|||
=> Penumbra.TempMods.Collections.TryGetValue( name, out collection )
|
||||
|| Penumbra.CollectionManager.Characters.TryGetValue( name, out collection );
|
||||
|
||||
// Check special collections given the actor.
|
||||
private static bool CollectionByActor( string name, GameObject* actor, [NotNullWhen( true )] out ModCollection? collection )
|
||||
{
|
||||
collection = null;
|
||||
// Check for the Yourself collection.
|
||||
if( actor->ObjectIndex == 0 || name == Dalamud.ClientState.LocalPlayer?.Name.ToString() )
|
||||
{
|
||||
collection = Penumbra.CollectionManager.ByType( CollectionType.Yourself );
|
||||
if( collection != null )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if( actor->IsCharacter() )
|
||||
{
|
||||
var character = ( Character* )actor;
|
||||
// Only handle human models.
|
||||
if( character->ModelCharaId == 0 )
|
||||
{
|
||||
// Check if the object is a non-player human NPC.
|
||||
if( actor->ObjectKind != ( byte )ObjectKind.Player )
|
||||
{
|
||||
collection = Penumbra.CollectionManager.ByType( CollectionType.NonPlayerCharacter );
|
||||
if( collection != null )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check the subrace. If it does not fit any or no subrace collection is set, check the player character collection.
|
||||
collection = ( SubRace )( ( Character* )actor )->CustomizeData[ 4 ] switch
|
||||
{
|
||||
SubRace.Midlander => Penumbra.CollectionManager.ByType( CollectionType.Midlander ),
|
||||
SubRace.Highlander => Penumbra.CollectionManager.ByType( CollectionType.Highlander ),
|
||||
SubRace.Wildwood => Penumbra.CollectionManager.ByType( CollectionType.Wildwood ),
|
||||
SubRace.Duskwight => Penumbra.CollectionManager.ByType( CollectionType.Duskwight ),
|
||||
SubRace.Plainsfolk => Penumbra.CollectionManager.ByType( CollectionType.Plainsfolk ),
|
||||
SubRace.Dunesfolk => Penumbra.CollectionManager.ByType( CollectionType.Dunesfolk ),
|
||||
SubRace.SeekerOfTheSun => Penumbra.CollectionManager.ByType( CollectionType.SeekerOfTheSun ),
|
||||
SubRace.KeeperOfTheMoon => Penumbra.CollectionManager.ByType( CollectionType.KeeperOfTheMoon ),
|
||||
SubRace.Seawolf => Penumbra.CollectionManager.ByType( CollectionType.Seawolf ),
|
||||
SubRace.Hellsguard => Penumbra.CollectionManager.ByType( CollectionType.Hellsguard ),
|
||||
SubRace.Raen => Penumbra.CollectionManager.ByType( CollectionType.Raen ),
|
||||
SubRace.Xaela => Penumbra.CollectionManager.ByType( CollectionType.Xaela ),
|
||||
SubRace.Helion => Penumbra.CollectionManager.ByType( CollectionType.Helion ),
|
||||
SubRace.Lost => Penumbra.CollectionManager.ByType( CollectionType.Lost ),
|
||||
SubRace.Rava => Penumbra.CollectionManager.ByType( CollectionType.Rava ),
|
||||
SubRace.Veena => Penumbra.CollectionManager.ByType( CollectionType.Veena ),
|
||||
_ => null,
|
||||
};
|
||||
collection ??= Penumbra.CollectionManager.ByType( CollectionType.PlayerCharacter );
|
||||
if( collection != null )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Update collections linked to Game/DrawObjects due to a change in collection configuration.
|
||||
private void CheckCollections( ModCollection.Type type, ModCollection? _1, ModCollection? _2, string? name )
|
||||
private void CheckCollections( CollectionType type, ModCollection? _1, ModCollection? _2, string? name )
|
||||
{
|
||||
if( type is not (ModCollection.Type.Character or ModCollection.Type.Default) )
|
||||
if( type is not (CollectionType.Character or CollectionType.Default) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,23 +324,34 @@ public class Penumbra : IDisposable
|
|||
return false;
|
||||
}
|
||||
|
||||
switch( type )
|
||||
foreach( var t in Enum.GetValues< CollectionType >() )
|
||||
{
|
||||
case "default":
|
||||
if( collection == CollectionManager.Default )
|
||||
{
|
||||
Dalamud.Chat.Print( $"{collection.Name} already is the default collection." );
|
||||
return false;
|
||||
}
|
||||
if( t is CollectionType.Inactive or CollectionType.Character
|
||||
|| !string.Equals( t.ToString(), type, StringComparison.OrdinalIgnoreCase ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CollectionManager.SetCollection( collection, ModCollection.Type.Default );
|
||||
Dalamud.Chat.Print( $"Set {collection.Name} as default collection." );
|
||||
return true;
|
||||
default:
|
||||
Dalamud.Chat.Print(
|
||||
"Second command argument is not default, the correct command format is: /penumbra collection default <collectionName>" );
|
||||
var oldCollection = CollectionManager.ByType( t );
|
||||
if( collection == oldCollection )
|
||||
{
|
||||
Dalamud.Chat.Print( $"{collection.Name} already is the {t.ToName()} Collection." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( oldCollection == null && t.IsSpecial() )
|
||||
{
|
||||
CollectionManager.CreateSpecialCollection( t );
|
||||
}
|
||||
|
||||
CollectionManager.SetCollection( collection, t, null );
|
||||
Dalamud.Chat.Print( $"Set {collection.Name} as {t.ToName()} Collection." );
|
||||
return true;
|
||||
}
|
||||
|
||||
Dalamud.Chat.Print(
|
||||
"Second command argument is not default, the correct command format is: /penumbra collection <collectionType> <collectionName>" );
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnCommand( string command, string rawArgs )
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
Penumbra.ModManager.ModMetaChanged += OnModMetaChange;
|
||||
Penumbra.ModManager.ModDiscoveryStarted += StoreCurrentSelection;
|
||||
Penumbra.ModManager.ModDiscoveryFinished += RestoreLastSelection;
|
||||
OnCollectionChange( ModCollection.Type.Current, null, Penumbra.CollectionManager.Current, null );
|
||||
OnCollectionChange( CollectionType.Current, null, Penumbra.CollectionManager.Current, null );
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
|
|
@ -354,9 +354,9 @@ public sealed partial class ModFileSystemSelector : FileSystemSelector< Mod, Mod
|
|||
OnSelectionChange( Selected, Selected, default );
|
||||
}
|
||||
|
||||
private void OnCollectionChange( ModCollection.Type type, ModCollection? oldCollection, ModCollection? newCollection, string? _ )
|
||||
private void OnCollectionChange( CollectionType collectionType, ModCollection? oldCollection, ModCollection? newCollection, string? _ )
|
||||
{
|
||||
if( type != ModCollection.Type.Current || oldCollection == newCollection )
|
||||
if( collectionType != CollectionType.Current || oldCollection == newCollection )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ public partial class ConfigWindow
|
|||
{
|
||||
if( _newCurrentCollection != null )
|
||||
{
|
||||
Penumbra.CollectionManager.SetCollection( _newCurrentCollection, ModCollection.Type.Current );
|
||||
Penumbra.CollectionManager.SetCollection( _newCurrentCollection, CollectionType.Current );
|
||||
_newCurrentCollection = null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using ImGuiNET;
|
|||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.UI;
|
||||
|
||||
|
|
@ -33,9 +34,10 @@ public partial class ConfigWindow
|
|||
|
||||
|
||||
// Input text fields.
|
||||
private string _newCollectionName = string.Empty;
|
||||
private bool _canAddCollection = false;
|
||||
private string _newCharacterName = string.Empty;
|
||||
private string _newCollectionName = string.Empty;
|
||||
private bool _canAddCollection = false;
|
||||
private string _newCharacterName = string.Empty;
|
||||
private CollectionType? _currentType = CollectionType.Yourself;
|
||||
|
||||
// Create a new collection that is either empty or a duplicate of the current collection.
|
||||
// Resets the new collection name.
|
||||
|
|
@ -104,7 +106,7 @@ public partial class ConfigWindow
|
|||
|
||||
private void DrawCurrentCollectionSelector()
|
||||
{
|
||||
DrawCollectionSelector( "##current", _window._inputTextWidth.X, ModCollection.Type.Current, false, null );
|
||||
DrawCollectionSelector( "##current", _window._inputTextWidth.X, CollectionType.Current, false, null );
|
||||
ImGui.SameLine();
|
||||
ImGuiUtil.LabeledHelpMarker( "Current Collection",
|
||||
"This collection will be modified when using the Installed Mods tab and making changes. It does not apply to anything by itself." );
|
||||
|
|
@ -112,12 +114,56 @@ public partial class ConfigWindow
|
|||
|
||||
private void DrawDefaultCollectionSelector()
|
||||
{
|
||||
DrawCollectionSelector( "##default", _window._inputTextWidth.X, ModCollection.Type.Default, true, null );
|
||||
DrawCollectionSelector( "##default", _window._inputTextWidth.X, CollectionType.Default, true, null );
|
||||
ImGui.SameLine();
|
||||
ImGuiUtil.LabeledHelpMarker( "Default Collection",
|
||||
"Mods in the default collection are loaded for any character that is not explicitly named in the character collections below.\n" );
|
||||
}
|
||||
|
||||
// We do not check for valid character names.
|
||||
private void DrawNewSpecialCollection()
|
||||
{
|
||||
const string description = "Special Collections apply to certain types of characters.\n"
|
||||
+ "All of them take precedence before the Default collection,\n"
|
||||
+ "but all character collections take precedence before them.";
|
||||
|
||||
ImGui.SetNextItemWidth( _window._inputTextWidth.X );
|
||||
if( _currentType == null || Penumbra.CollectionManager.ByType( _currentType.Value ) != null )
|
||||
{
|
||||
_currentType = CollectionTypeExtensions.Special.FindFirst( t => Penumbra.CollectionManager.ByType( t ) == null, out var t2 )
|
||||
? t2
|
||||
: null;
|
||||
}
|
||||
|
||||
if( _currentType == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using( var combo = ImRaii.Combo( "##NewSpecial", _currentType.Value.ToName() ) )
|
||||
{
|
||||
if( combo )
|
||||
{
|
||||
foreach( var type in CollectionTypeExtensions.Special.Where( t => Penumbra.CollectionManager.ByType( t ) == null ) )
|
||||
{
|
||||
if( ImGui.Selectable( type.ToName(), type == _currentType.Value ) )
|
||||
{
|
||||
_currentType = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
var disabled = _currentType == null;
|
||||
var tt = disabled ? "Please select a special collection type before creating the collection.\n\n" + description : description;
|
||||
if( ImGuiUtil.DrawDisabledButton( "Create New Special Collection", Vector2.Zero, tt, disabled ) )
|
||||
{
|
||||
Penumbra.CollectionManager.CreateSpecialCollection( _currentType!.Value );
|
||||
_currentType = null;
|
||||
}
|
||||
}
|
||||
|
||||
// We do not check for valid character names.
|
||||
private void DrawNewCharacterCollection()
|
||||
{
|
||||
|
|
@ -145,14 +191,36 @@ public partial class ConfigWindow
|
|||
ImGui.Dummy( _window._defaultSpace );
|
||||
DrawDefaultCollectionSelector();
|
||||
ImGui.Dummy( _window._defaultSpace );
|
||||
foreach( var type in CollectionTypeExtensions.Special )
|
||||
{
|
||||
var collection = Penumbra.CollectionManager.ByType( type );
|
||||
if( collection != null )
|
||||
{
|
||||
using var id = ImRaii.PushId( ( int )type );
|
||||
DrawCollectionSelector( string.Empty, _window._inputTextWidth.X, type, true, null );
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), _window._iconButtonSize, string.Empty,
|
||||
false, true ) )
|
||||
{
|
||||
Penumbra.CollectionManager.RemoveSpecialCollection( type );
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGuiUtil.LabeledHelpMarker( type.ToName(), type.ToDescription() );
|
||||
}
|
||||
}
|
||||
|
||||
DrawNewSpecialCollection();
|
||||
ImGui.Dummy( _window._defaultSpace );
|
||||
|
||||
foreach( var name in Penumbra.CollectionManager.Characters.Keys.OrderBy( k => k ).ToArray() )
|
||||
{
|
||||
using var id = ImRaii.PushId( name );
|
||||
DrawCollectionSelector( string.Empty, _window._inputTextWidth.X, ModCollection.Type.Character, true, name );
|
||||
DrawCollectionSelector( string.Empty, _window._inputTextWidth.X, CollectionType.Character, true, name );
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), _window._iconButtonSize, string.Empty,
|
||||
false,
|
||||
true ) )
|
||||
false, true ) )
|
||||
{
|
||||
Penumbra.CollectionManager.RemoveCharacterCollection( name );
|
||||
}
|
||||
|
|
@ -163,7 +231,7 @@ public partial class ConfigWindow
|
|||
}
|
||||
|
||||
DrawNewCharacterCollection();
|
||||
ImGui.NewLine();
|
||||
ImGui.Dummy( _window._defaultSpace );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,18 +93,13 @@ public partial class ConfigWindow
|
|||
}
|
||||
|
||||
// Draw a collection selector of a certain width for a certain type.
|
||||
private static void DrawCollectionSelector( string label, float width, ModCollection.Type type, bool withEmpty, string? characterName )
|
||||
private static void DrawCollectionSelector( string label, float width, CollectionType collectionType, bool withEmpty,
|
||||
string? characterName )
|
||||
{
|
||||
ImGui.SetNextItemWidth( width );
|
||||
var current = type switch
|
||||
{
|
||||
ModCollection.Type.Default => Penumbra.CollectionManager.Default,
|
||||
ModCollection.Type.Character => Penumbra.CollectionManager.Character( characterName ?? string.Empty ),
|
||||
ModCollection.Type.Current => Penumbra.CollectionManager.Current,
|
||||
_ => throw new ArgumentOutOfRangeException( nameof( type ), type, null ),
|
||||
};
|
||||
|
||||
using var combo = ImRaii.Combo( label, current.Name );
|
||||
var current = Penumbra.CollectionManager.ByType( collectionType, characterName );
|
||||
using var combo = ImRaii.Combo( label, current?.Name ?? string.Empty );
|
||||
if( combo )
|
||||
{
|
||||
foreach( var collection in Penumbra.CollectionManager.GetEnumeratorWithEmpty().Skip( withEmpty ? 0 : 1 ).OrderBy( c => c.Name ) )
|
||||
|
|
@ -112,7 +107,7 @@ public partial class ConfigWindow
|
|||
using var id = ImRaii.PushId( collection.Index );
|
||||
if( ImGui.Selectable( collection.Name, collection == current ) )
|
||||
{
|
||||
Penumbra.CollectionManager.SetCollection( collection, type, characterName );
|
||||
Penumbra.CollectionManager.SetCollection( collection, collectionType, characterName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -140,7 +135,7 @@ public partial class ConfigWindow
|
|||
}
|
||||
|
||||
// Add Penumbra Root. This is not updated if the root changes right now.
|
||||
fileManager.CustomSideBarItems.Add( ("Root Directory", Penumbra.Config.ModDirectory, FontAwesomeIcon.Gamepad, 0) );
|
||||
fileManager.CustomSideBarItems.Add( ( "Root Directory", Penumbra.Config.ModDirectory, FontAwesomeIcon.Gamepad, 0 ) );
|
||||
|
||||
// Remove Videos and Music.
|
||||
fileManager.CustomSideBarItems.Add( ( "Videos", string.Empty, 0, -1 ) );
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public partial class ConfigWindow
|
|||
ImGui.SameLine();
|
||||
DrawInheritedCollectionButton( 3 * buttonSize );
|
||||
ImGui.SameLine();
|
||||
DrawCollectionSelector( "##collectionSelector", 2 * buttonSize.X, ModCollection.Type.Current, false, null );
|
||||
DrawCollectionSelector( "##collectionSelector", 2 * buttonSize.X, CollectionType.Current, false, null );
|
||||
if( !Penumbra.CollectionManager.CurrentCollectionInUse )
|
||||
{
|
||||
ImGuiUtil.DrawTextButton( "The currently selected collection is not used in any way.", -Vector2.UnitX, Colors.PressEnterWarningBg );
|
||||
|
|
@ -79,7 +79,7 @@ public partial class ConfigWindow
|
|||
: "Set the current collection to the configured default collection.";
|
||||
if( ImGuiUtil.DrawDisabledButton( name, width, tt, isCurrent || isEmpty ) )
|
||||
{
|
||||
Penumbra.CollectionManager.SetCollection( Penumbra.CollectionManager.Default, ModCollection.Type.Current );
|
||||
Penumbra.CollectionManager.SetCollection( Penumbra.CollectionManager.Default, CollectionType.Current );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ public partial class ConfigWindow
|
|||
};
|
||||
if( ImGuiUtil.DrawDisabledButton( name, width, tt, noModSelected || !modInherited ) )
|
||||
{
|
||||
Penumbra.CollectionManager.SetCollection( collection, ModCollection.Type.Current );
|
||||
Penumbra.CollectionManager.SetCollection( collection, CollectionType.Current );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue