Add special collections.

This commit is contained in:
Ottermandias 2022-06-26 13:44:15 +02:00
parent ec91755065
commit 549f8ce4b4
11 changed files with 486 additions and 86 deletions

View file

@ -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();
}

View file

@ -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;
}

View 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,
};
}

View file

@ -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.

View file

@ -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;
}

View file

@ -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 )

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 );
}
}

View file

@ -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 ) );

View file

@ -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 );
}
}