mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Add support for retainer collections, fix deleted assignments not updating identifiers.
This commit is contained in:
parent
e47ca842b2
commit
304b75e7d2
8 changed files with 233 additions and 107 deletions
|
|
@ -25,7 +25,7 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
|||
// @formatter:on
|
||||
|
||||
public ActorIdentifier CreatePermanent()
|
||||
=> new(Type, Kind, Index, DataId, PlayerName.Clone());
|
||||
=> new(Type, Kind, Index, DataId, PlayerName.IsEmpty ? PlayerName : PlayerName.Clone());
|
||||
|
||||
public bool Equals(ActorIdentifier other)
|
||||
{
|
||||
|
|
@ -35,10 +35,12 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
|||
return Type switch
|
||||
{
|
||||
IdentifierType.Player => HomeWorld == other.HomeWorld && PlayerName.EqualsCi(other.PlayerName),
|
||||
IdentifierType.Retainer => PlayerName.EqualsCi(other.PlayerName),
|
||||
IdentifierType.Owned => HomeWorld == other.HomeWorld && PlayerName.EqualsCi(other.PlayerName) && Manager.DataIdEquals(this, other),
|
||||
IdentifierType.Special => Special == other.Special,
|
||||
IdentifierType.Npc => Manager.DataIdEquals(this, other)
|
||||
&& (Index == other.Index || Index == ushort.MaxValue || other.Index == ushort.MaxValue),
|
||||
IdentifierType.UnkObject => PlayerName.EqualsCi(other.PlayerName) && Index == other.Index,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
|
@ -53,19 +55,23 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
|||
=> !lhs.Equals(rhs);
|
||||
|
||||
public bool IsValid
|
||||
=> Type != IdentifierType.Invalid;
|
||||
=> Type is not (IdentifierType.UnkObject or IdentifierType.Invalid);
|
||||
|
||||
public override string ToString()
|
||||
=> Manager?.ToString(this)
|
||||
?? Type switch
|
||||
{
|
||||
IdentifierType.Player => $"{PlayerName} ({HomeWorld})",
|
||||
IdentifierType.Retainer => $"{PlayerName} (Retainer)",
|
||||
IdentifierType.Owned => $"{PlayerName}s {Kind.ToName()} {DataId} ({HomeWorld})",
|
||||
IdentifierType.Special => Special.ToName(),
|
||||
IdentifierType.Npc =>
|
||||
Index == ushort.MaxValue
|
||||
? $"{Kind.ToName()} #{DataId}"
|
||||
: $"{Kind.ToName()} #{DataId} at {Index}",
|
||||
IdentifierType.UnkObject => PlayerName.IsEmpty
|
||||
? $"Unknown Object at {Index}"
|
||||
: $"{PlayerName} at {Index}",
|
||||
_ => "Invalid",
|
||||
};
|
||||
|
||||
|
|
@ -73,9 +79,11 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
|||
=> Type switch
|
||||
{
|
||||
IdentifierType.Player => HashCode.Combine(IdentifierType.Player, PlayerName, HomeWorld),
|
||||
IdentifierType.Retainer => HashCode.Combine(IdentifierType.Player, PlayerName),
|
||||
IdentifierType.Owned => HashCode.Combine(IdentifierType.Owned, Kind, PlayerName, HomeWorld, DataId),
|
||||
IdentifierType.Special => HashCode.Combine(IdentifierType.Special, Special),
|
||||
IdentifierType.Npc => HashCode.Combine(IdentifierType.Npc, Kind, DataId),
|
||||
IdentifierType.UnkObject => HashCode.Combine(IdentifierType.UnkObject, PlayerName, Index),
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
|
|
@ -98,6 +106,9 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
|||
ret.Add(nameof(PlayerName), PlayerName.ToString());
|
||||
ret.Add(nameof(HomeWorld), HomeWorld);
|
||||
return ret;
|
||||
case IdentifierType.Retainer:
|
||||
ret.Add(nameof(PlayerName), PlayerName.ToString());
|
||||
return ret;
|
||||
case IdentifierType.Owned:
|
||||
ret.Add(nameof(PlayerName), PlayerName.ToString());
|
||||
ret.Add(nameof(HomeWorld), HomeWorld);
|
||||
|
|
@ -113,6 +124,10 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
|||
ret.Add(nameof(Index), Index);
|
||||
ret.Add(nameof(DataId), DataId);
|
||||
return ret;
|
||||
case IdentifierType.UnkObject:
|
||||
ret.Add(nameof(PlayerName), PlayerName.ToString());
|
||||
ret.Add(nameof(Index), Index);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -163,9 +178,11 @@ public static class ActorManagerExtensions
|
|||
=> type switch
|
||||
{
|
||||
IdentifierType.Player => "Player",
|
||||
IdentifierType.Retainer => "Retainer (Bell)",
|
||||
IdentifierType.Owned => "Owned NPC",
|
||||
IdentifierType.Special => "Special Actor",
|
||||
IdentifierType.Npc => "NPC",
|
||||
IdentifierType.UnkObject => "Unknown Object",
|
||||
_ => "Invalid",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ public partial class ActorManager
|
|||
var homeWorld = data[nameof(ActorIdentifier.HomeWorld)]?.ToObject<ushort>() ?? 0;
|
||||
return CreatePlayer(name, homeWorld);
|
||||
}
|
||||
case IdentifierType.Retainer:
|
||||
{
|
||||
var name = ByteString.FromStringUnsafe(data[nameof(ActorIdentifier.PlayerName)]?.ToObject<string>(), false);
|
||||
return CreateRetainer(name);
|
||||
}
|
||||
case IdentifierType.Owned:
|
||||
{
|
||||
var name = ByteString.FromStringUnsafe(data[nameof(ActorIdentifier.PlayerName)]?.ToObject<string>(), false);
|
||||
|
|
@ -48,6 +53,12 @@ public partial class ActorManager
|
|||
var dataId = data[nameof(ActorIdentifier.DataId)]?.ToObject<uint>() ?? 0;
|
||||
return CreateNpc(kind, dataId, index);
|
||||
}
|
||||
case IdentifierType.UnkObject:
|
||||
{
|
||||
var index = data[nameof(ActorIdentifier.Index)]?.ToObject<ushort>() ?? ushort.MaxValue;
|
||||
var name = ByteString.FromStringUnsafe(data[nameof(ActorIdentifier.PlayerName)]?.ToObject<string>(), false);
|
||||
return CreateIndividualUnchecked(IdentifierType.UnkObject, name, index, ObjectKind.None, 0);
|
||||
}
|
||||
default: return ActorIdentifier.Invalid;
|
||||
}
|
||||
}
|
||||
|
|
@ -68,6 +79,7 @@ public partial class ActorManager
|
|||
IdentifierType.Player => id.HomeWorld != _clientState.LocalPlayer?.HomeWorld.Id
|
||||
? $"{id.PlayerName} ({ToWorldName(id.HomeWorld)})"
|
||||
: id.PlayerName.ToString(),
|
||||
IdentifierType.Retainer => id.PlayerName.ToString(),
|
||||
IdentifierType.Owned => id.HomeWorld != _clientState.LocalPlayer?.HomeWorld.Id
|
||||
? $"{id.PlayerName} ({ToWorldName(id.HomeWorld)})'s {ToName(id.Kind, id.DataId)}"
|
||||
: $"{id.PlayerName}s {ToName(id.Kind, id.DataId)}",
|
||||
|
|
@ -76,6 +88,9 @@ public partial class ActorManager
|
|||
id.Index == ushort.MaxValue
|
||||
? ToName(id.Kind, id.DataId)
|
||||
: $"{ToName(id.Kind, id.DataId)} at {id.Index}",
|
||||
IdentifierType.UnkObject => id.PlayerName.IsEmpty
|
||||
? $"Unknown Object at {id.Index}"
|
||||
: $"{id.PlayerName} at {id.Index}",
|
||||
_ => "Invalid",
|
||||
};
|
||||
}
|
||||
|
|
@ -188,7 +203,19 @@ public partial class ActorManager
|
|||
: CreateIndividualUnchecked(IdentifierType.Owned, new ByteString(owner->GameObject.Name), owner->HomeWorld,
|
||||
(ObjectKind)actor->ObjectKind, dataId);
|
||||
}
|
||||
default: return ActorIdentifier.Invalid;
|
||||
case ObjectKind.Retainer:
|
||||
{
|
||||
var name = new ByteString(actor->Name);
|
||||
return check
|
||||
? CreateRetainer(name)
|
||||
: CreateIndividualUnchecked(IdentifierType.Retainer, name, 0, ObjectKind.None, uint.MaxValue);
|
||||
}
|
||||
default:
|
||||
{
|
||||
var name = new ByteString(actor->Name);
|
||||
var index = actor->ObjectIndex;
|
||||
return CreateIndividualUnchecked(IdentifierType.UnkObject, name, index, ObjectKind.None, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,9 +241,11 @@ public partial class ActorManager
|
|||
=> type switch
|
||||
{
|
||||
IdentifierType.Player => CreatePlayer(name, homeWorld),
|
||||
IdentifierType.Retainer => CreateRetainer(name),
|
||||
IdentifierType.Owned => CreateOwned(name, homeWorld, kind, dataId),
|
||||
IdentifierType.Special => CreateSpecial((SpecialActor)homeWorld),
|
||||
IdentifierType.Npc => CreateNpc(kind, dataId, homeWorld),
|
||||
IdentifierType.UnkObject => CreateIndividualUnchecked(IdentifierType.UnkObject, name, homeWorld, ObjectKind.None, 0),
|
||||
_ => ActorIdentifier.Invalid,
|
||||
};
|
||||
|
||||
|
|
@ -234,6 +263,14 @@ public partial class ActorManager
|
|||
return new ActorIdentifier(IdentifierType.Player, ObjectKind.Player, homeWorld, 0, name);
|
||||
}
|
||||
|
||||
public ActorIdentifier CreateRetainer(ByteString name)
|
||||
{
|
||||
if (!VerifyRetainerName(name.Span))
|
||||
return ActorIdentifier.Invalid;
|
||||
|
||||
return new ActorIdentifier(IdentifierType.Retainer, ObjectKind.Retainer, 0, 0, name);
|
||||
}
|
||||
|
||||
public ActorIdentifier CreateSpecial(SpecialActor actor)
|
||||
{
|
||||
if (!VerifySpecial(actor))
|
||||
|
|
@ -270,37 +307,7 @@ public partial class ActorManager
|
|||
if (splitIndex < 0 || name[(splitIndex + 1)..].IndexOf((byte)' ') >= 0)
|
||||
return false;
|
||||
|
||||
static bool CheckNamePart(ReadOnlySpan<byte> part)
|
||||
{
|
||||
// Each name part at least 2 and at most 15 characters.
|
||||
if (part.Length is < 2 or > 15)
|
||||
return false;
|
||||
|
||||
// Each part starting with capitalized letter.
|
||||
if (part[0] is < (byte)'A' or > (byte)'Z')
|
||||
return false;
|
||||
|
||||
// Every other symbol needs to be lowercase letter, hyphen or apostrophe.
|
||||
var last = (byte)'\0';
|
||||
for (var i = 1; i < part.Length; ++i)
|
||||
{
|
||||
var current = part[i];
|
||||
if (current is not ((byte)'\'' or (byte)'-' or (>= (byte)'a' and <= (byte)'z')))
|
||||
return false;
|
||||
|
||||
// Hyphens can not be used in succession, after or before apostrophes or as the last symbol.
|
||||
if (last is (byte)'\'' && current is (byte)'-')
|
||||
return false;
|
||||
if (last is (byte)'-' && current is (byte)'-' or (byte)'\'')
|
||||
return false;
|
||||
|
||||
last = current;
|
||||
}
|
||||
|
||||
return part[^1] != (byte)'-';
|
||||
}
|
||||
|
||||
return CheckNamePart(name[..splitIndex]) && CheckNamePart(name[(splitIndex + 1)..]);
|
||||
return CheckNamePart(name[..splitIndex], 2, 15) && CheckNamePart(name[(splitIndex + 1)..], 2, 15);
|
||||
}
|
||||
|
||||
/// <summary> Checks SE naming rules. </summary>
|
||||
|
|
@ -315,10 +322,21 @@ public partial class ActorManager
|
|||
if (splitIndex < 0 || name[(splitIndex + 1)..].IndexOf(' ') >= 0)
|
||||
return false;
|
||||
|
||||
static bool CheckNamePart(ReadOnlySpan<char> part)
|
||||
return CheckNamePart(name[..splitIndex], 2, 15) && CheckNamePart(name[(splitIndex + 1)..], 2, 15);
|
||||
}
|
||||
|
||||
/// <summary> Checks SE naming rules. </summary>
|
||||
public static bool VerifyRetainerName(ReadOnlySpan<byte> name)
|
||||
=> CheckNamePart(name, 3, 20);
|
||||
|
||||
/// <summary> Checks SE naming rules. </summary>
|
||||
public static bool VerifyRetainerName(ReadOnlySpan<char> name)
|
||||
=> CheckNamePart(name, 3, 20);
|
||||
|
||||
private static bool CheckNamePart(ReadOnlySpan<char> part, int minLength, int maxLength)
|
||||
{
|
||||
// Each name part at least 2 and at most 15 characters.
|
||||
if (part.Length is < 2 or > 15)
|
||||
// Each name part at least 2 and at most 15 characters for players, and at least 3 and at most 20 characters for retainers.
|
||||
if (part.Length < minLength || part.Length > maxLength)
|
||||
return false;
|
||||
|
||||
// Each part starting with capitalized letter.
|
||||
|
|
@ -345,7 +363,34 @@ public partial class ActorManager
|
|||
return part[^1] != '-';
|
||||
}
|
||||
|
||||
return CheckNamePart(name[..splitIndex]) && CheckNamePart(name[(splitIndex + 1)..]);
|
||||
private static bool CheckNamePart(ReadOnlySpan<byte> part, int minLength, int maxLength)
|
||||
{
|
||||
// Each name part at least 2 and at most 15 characters for players, and at least 3 and at most 20 characters for retainers.
|
||||
if (part.Length < minLength || part.Length > maxLength)
|
||||
return false;
|
||||
|
||||
// Each part starting with capitalized letter.
|
||||
if (part[0] is < (byte)'A' or > (byte)'Z')
|
||||
return false;
|
||||
|
||||
// Every other symbol needs to be lowercase letter, hyphen or apostrophe.
|
||||
var last = (byte)'\0';
|
||||
for (var i = 1; i < part.Length; ++i)
|
||||
{
|
||||
var current = part[i];
|
||||
if (current is not ((byte)'\'' or (byte)'-' or (>= (byte)'a' and <= (byte)'z')))
|
||||
return false;
|
||||
|
||||
// Hyphens can not be used in succession, after or before apostrophes or as the last symbol.
|
||||
if (last is (byte)'\'' && current is (byte)'-')
|
||||
return false;
|
||||
if (last is (byte)'-' && current is (byte)'-' or (byte)'\'')
|
||||
return false;
|
||||
|
||||
last = current;
|
||||
}
|
||||
|
||||
return part[^1] != (byte)'-';
|
||||
}
|
||||
|
||||
/// <summary> Checks if the world is a valid public world or ushort.MaxValue (any world). </summary>
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@ public enum IdentifierType : byte
|
|||
Owned,
|
||||
Special,
|
||||
Npc,
|
||||
Retainer,
|
||||
UnkObject,
|
||||
};
|
||||
|
|
@ -28,6 +28,20 @@ public sealed partial class IndividualCollections : IReadOnlyList< (string Displ
|
|||
switch( identifier.Type )
|
||||
{
|
||||
case IdentifierType.Player: return CheckWorlds( identifier, out collection );
|
||||
case IdentifierType.Retainer:
|
||||
{
|
||||
if( _individuals.TryGetValue( identifier, out collection ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if( Penumbra.Config.UseOwnerNameForCharacterCollection )
|
||||
{
|
||||
return CheckWorlds( _actorManager.GetCurrentPlayer(), out collection );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case IdentifierType.Owned:
|
||||
{
|
||||
if( CheckWorlds( identifier, out collection! ) )
|
||||
|
|
|
|||
|
|
@ -62,8 +62,15 @@ public sealed partial class IndividualCollections
|
|||
return AddResult.Invalid;
|
||||
}
|
||||
|
||||
var identifier = _actorManager.CreatePlayer( playerName, homeWorld );
|
||||
identifiers = new[] { identifier };
|
||||
identifiers = new[] { _actorManager.CreatePlayer( playerName, homeWorld ) };
|
||||
break;
|
||||
case IdentifierType.Retainer:
|
||||
if( !ByteString.FromString( name, out var retainerName ) )
|
||||
{
|
||||
return AddResult.Invalid;
|
||||
}
|
||||
|
||||
identifiers = new[] { _actorManager.CreateRetainer( retainerName ) };
|
||||
break;
|
||||
case IdentifierType.Owned:
|
||||
if( !ByteString.FromString( name, out var ownerName ) )
|
||||
|
|
@ -110,6 +117,7 @@ public sealed partial class IndividualCollections
|
|||
{
|
||||
IdentifierType.Player => new[] { identifier.CreatePermanent() },
|
||||
IdentifierType.Special => new[] { identifier },
|
||||
IdentifierType.Retainer => new[] { identifier.CreatePermanent() },
|
||||
IdentifierType.Owned => CreateNpcs( _actorManager, identifier.CreatePermanent() ),
|
||||
IdentifierType.Npc => CreateNpcs( _actorManager, identifier ),
|
||||
_ => Array.Empty< ActorIdentifier >(),
|
||||
|
|
@ -198,6 +206,7 @@ public sealed partial class IndividualCollections
|
|||
return identifier.Type switch
|
||||
{
|
||||
IdentifierType.Player => $"{identifier.PlayerName} ({_actorManager.ToWorldName( identifier.HomeWorld )})",
|
||||
IdentifierType.Retainer => $"{identifier.PlayerName} (Retainer)",
|
||||
IdentifierType.Owned =>
|
||||
$"{identifier.PlayerName} ({_actorManager.ToWorldName( identifier.HomeWorld )})'s {_actorManager.ToName( identifier.Kind, identifier.DataId )}",
|
||||
IdentifierType.Npc => $"{_actorManager.ToName( identifier.Kind, identifier.DataId )} ({identifier.Kind.ToName()})",
|
||||
|
|
|
|||
|
|
@ -76,12 +76,16 @@ public partial class ConfigWindow
|
|||
private readonly NpcCombo _enpcCombo = new("##enpcCombo", Penumbra.Actors.ENpcs);
|
||||
|
||||
private const string NewPlayerTooltipEmpty = "Please enter a valid player name and choose an available world or 'Any World'.";
|
||||
private const string NewRetainerTooltipEmpty = "Please enter a valid retainer name.";
|
||||
private const string NewPlayerTooltipInvalid = "The entered name is not a valid name for a player character.";
|
||||
private const string NewRetainerTooltipInvalid = "The entered name is not a valid name for a retainer.";
|
||||
private const string AlreadyAssigned = "The Individual you specified has already been assigned a collection.";
|
||||
private const string NewNpcTooltipEmpty = "Please select a valid NPC from the drop down menu first.";
|
||||
|
||||
private ActorIdentifier[] _newPlayerIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
private string _newPlayerTooltip = NewPlayerTooltipEmpty;
|
||||
private ActorIdentifier[] _newRetainerIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
private string _newRetainerTooltip = NewRetainerTooltipEmpty;
|
||||
private ActorIdentifier[] _newNpcIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
private string _newNpcTooltip = NewNpcTooltipEmpty;
|
||||
private ActorIdentifier[] _newOwnedIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
|
|
@ -201,16 +205,22 @@ public partial class ConfigWindow
|
|||
|
||||
private bool DrawNewOwnedCollection( Vector2 buttonWidth )
|
||||
{
|
||||
var oldPos = ImGui.GetCursorPos();
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPos( ImGui.GetCursorPos() + new Vector2( -ImGui.GetStyle().ItemSpacing.X / 2, ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.Y ) / 2 );
|
||||
if( ImGuiUtil.DrawDisabledButton( "Assign Owned NPC", buttonWidth, _newOwnedTooltip, _newOwnedIdentifiers.Length == 0 || _newOwnedTooltip.Length > 0 ) )
|
||||
{
|
||||
Penumbra.CollectionManager.Individuals.Add( _newOwnedIdentifiers, Penumbra.CollectionManager.Default );
|
||||
return true;
|
||||
}
|
||||
|
||||
ImGui.SetCursorPos( oldPos );
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool DrawNewRetainerCollection( Vector2 buttonWidth )
|
||||
{
|
||||
if( ImGuiUtil.DrawDisabledButton( "Assign Bell Retainer", buttonWidth, _newRetainerTooltip, _newRetainerIdentifiers.Length == 0 || _newRetainerTooltip.Length > 0 ) )
|
||||
{
|
||||
Penumbra.CollectionManager.Individuals.Add( _newRetainerIdentifiers, Penumbra.CollectionManager.Default );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -229,12 +239,18 @@ public partial class ConfigWindow
|
|||
private void DrawNewIndividualCollection()
|
||||
{
|
||||
var width = ( _window._inputTextWidth.X - 2 * ImGui.GetStyle().ItemSpacing.X ) / 3;
|
||||
var buttonWidth = new Vector2( 90 * ImGuiHelpers.GlobalScale, 0 );
|
||||
|
||||
var buttonWidth1 = new Vector2( 90 * ImGuiHelpers.GlobalScale, 0 );
|
||||
var buttonWidth2 = new Vector2( 120 * ImGuiHelpers.GlobalScale, 0 );
|
||||
|
||||
var combo = GetNpcCombo( _newKind );
|
||||
var change = DrawNewPlayerCollection( buttonWidth, width );
|
||||
change |= DrawNewOwnedCollection( Vector2.Zero );
|
||||
change |= DrawNewNpcCollection( combo, buttonWidth, width );
|
||||
var change = DrawNewPlayerCollection( buttonWidth1, width );
|
||||
ImGui.SameLine();
|
||||
change |= DrawNewRetainerCollection( buttonWidth2 );
|
||||
|
||||
change |= DrawNewNpcCollection( combo, buttonWidth1, width );
|
||||
ImGui.SameLine();
|
||||
change |= DrawNewOwnedCollection( buttonWidth2 );
|
||||
|
||||
if( change )
|
||||
{
|
||||
|
|
@ -253,6 +269,14 @@ public partial class ConfigWindow
|
|||
IndividualCollections.AddResult.AlreadySet => AlreadyAssigned,
|
||||
_ => string.Empty,
|
||||
};
|
||||
_newRetainerTooltip = Penumbra.CollectionManager.Individuals.CanAdd( IdentifierType.Retainer, _newCharacterName, _worldCombo.CurrentSelection.Key, ObjectKind.None,
|
||||
Array.Empty< uint >(), out _newRetainerIdentifiers ) switch
|
||||
{
|
||||
_ when _newCharacterName.Length == 0 => NewRetainerTooltipEmpty,
|
||||
IndividualCollections.AddResult.Invalid => NewRetainerTooltipInvalid,
|
||||
IndividualCollections.AddResult.AlreadySet => AlreadyAssigned,
|
||||
_ => string.Empty,
|
||||
};
|
||||
if( combo.CurrentSelection.Ids != null )
|
||||
{
|
||||
_newNpcTooltip = Penumbra.CollectionManager.Individuals.CanAdd( IdentifierType.Npc, string.Empty, ushort.MaxValue, _newKind,
|
||||
|
|
@ -278,5 +302,11 @@ public partial class ConfigWindow
|
|||
_newOwnedIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateIdentifiers( CollectionType type, ModCollection? _1, ModCollection? _2, string _3 )
|
||||
{
|
||||
if( type == CollectionType.Individual )
|
||||
UpdateIdentifiers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Components;
|
||||
|
|
@ -13,12 +14,19 @@ namespace Penumbra.UI;
|
|||
public partial class ConfigWindow
|
||||
{
|
||||
// Encapsulate for less pollution.
|
||||
private partial class CollectionsTab
|
||||
private partial class CollectionsTab : IDisposable
|
||||
{
|
||||
private readonly ConfigWindow _window;
|
||||
|
||||
public CollectionsTab( ConfigWindow window )
|
||||
=> _window = window;
|
||||
{
|
||||
_window = window;
|
||||
|
||||
Penumbra.CollectionManager.CollectionChanged += UpdateIdentifiers;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> Penumbra.CollectionManager.CollectionChanged -= UpdateIdentifiers;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ public sealed partial class ConfigWindow : Window, IDisposable
|
|||
{
|
||||
_selector.Dispose();
|
||||
_modPanel.Dispose();
|
||||
_collectionsTab.Dispose();
|
||||
ModEditPopup.Dispose();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue