diff --git a/CustomizePlus/Api/CustomizePlusIpc.Profile.cs b/CustomizePlus/Api/CustomizePlusIpc.Profile.cs index 952927f..221bbf6 100644 --- a/CustomizePlus/Api/CustomizePlusIpc.Profile.cs +++ b/CustomizePlus/Api/CustomizePlusIpc.Profile.cs @@ -46,7 +46,7 @@ public partial class CustomizePlusIpc .Select(x => { string path = _profileFileSystem.FindLeaf(x, out var leaf) ? leaf.FullName() : x.Name.Text; - return (x.UniqueId, x.Name.Text, path, x.CharacterName.Text, x.Enabled); + return (x.UniqueId, x.Name.Text, path, x.Character.ToNameWithoutOwnerName(), x.Enabled); //todo: proper update to v5 }) .ToList(); } @@ -134,7 +134,7 @@ public partial class CustomizePlusIpc if (actor == null || !actor.Value.Valid || !actor.Value.IsCharacter) return ((int)ErrorCode.InvalidCharacter, null); - var profile = _profileManager.GetProfileByCharacterName(actor.Value.Utf8Name.ToString(), true); + var profile = _profileManager.GetProfileByActor(actor.Value, true); if (profile == null) return ((int)ErrorCode.ProfileNotFound, null); diff --git a/CustomizePlus/Api/Data/IPCCharacterProfile.cs b/CustomizePlus/Api/Data/IPCCharacterProfile.cs index 727e73e..c0f75c2 100644 --- a/CustomizePlus/Api/Data/IPCCharacterProfile.cs +++ b/CustomizePlus/Api/Data/IPCCharacterProfile.cs @@ -1,5 +1,6 @@ using CustomizePlus.Configuration.Data.Version3; using CustomizePlus.Core.Data; +using CustomizePlus.GameData.Extensions; using CustomizePlus.Profiles.Data; using CustomizePlus.Templates.Data; using System; @@ -23,7 +24,7 @@ public class IPCCharacterProfile { var ipcProfile = new IPCCharacterProfile { - CharacterName = profile.CharacterName, + CharacterName = profile.Character.ToNameWithoutOwnerName(), //todo: proper update to v5 Bones = new Dictionary() }; @@ -48,7 +49,7 @@ public class IPCCharacterProfile var fullProfile = new Profile { Name = $"{profile.CharacterName}'s IPC profile", - CharacterName = profile.CharacterName, + // CharacterName = profile.CharacterName, //todo: proper update to v5 CreationDate = DateTimeOffset.UtcNow, ModifiedDate = DateTimeOffset.UtcNow, Enabled = true, diff --git a/CustomizePlus/Armatures/Services/ArmatureManager.cs b/CustomizePlus/Armatures/Services/ArmatureManager.cs index 348e4e2..ece42c0 100644 --- a/CustomizePlus/Armatures/Services/ArmatureManager.cs +++ b/CustomizePlus/Armatures/Services/ArmatureManager.cs @@ -443,9 +443,10 @@ public unsafe sealed class ArmatureManager : IDisposable type is not ProfileChanged.Type.Deleted && type is not ProfileChanged.Type.TemporaryProfileAdded && type is not ProfileChanged.Type.TemporaryProfileDeleted && - type is not ProfileChanged.Type.ChangedCharacterName && + type is not ProfileChanged.Type.ChangedCharacter && type is not ProfileChanged.Type.ChangedDefaultProfile && - type is not ProfileChanged.Type.LimitLookupToOwnedChanged) + type is not ProfileChanged.Type.LimitLookupToOwnedChanged && + type is not ProfileChanged.Type.ApplyToCurrentlyActiveCharacterChanged) return; if (type == ProfileChanged.Type.ChangedDefaultProfile) @@ -488,10 +489,10 @@ public unsafe sealed class ArmatureManager : IDisposable return; } - if (string.IsNullOrWhiteSpace(profile.CharacterName)) + if (!profile.Character.IsValid) return; - foreach (var armature in GetArmaturesForCharacterName(profile.CharacterName)) + foreach (var armature in GetArmaturesForCharacter(profile.Character)) { armature.IsPendingProfileRebind = true; _logger.Debug($"ArmatureManager.OnProfileChange profile {profile} toggled, planning rebind for armature {armature}"); @@ -502,10 +503,10 @@ public unsafe sealed class ArmatureManager : IDisposable if (type == ProfileChanged.Type.TemporaryProfileAdded) { - if (!profile.TemporaryActor.IsValid || !Armatures.ContainsKey(profile.TemporaryActor)) + if (!profile.Character.IsValid || !Armatures.ContainsKey(profile.Character)) //todo: any world support return; - var armature = Armatures[profile.TemporaryActor]; + var armature = Armatures[profile.Character]; if (armature.Profile == profile) return; @@ -518,10 +519,11 @@ public unsafe sealed class ArmatureManager : IDisposable return; } - if (type == ProfileChanged.Type.ChangedCharacterName || + if (type == ProfileChanged.Type.ChangedCharacter || type == ProfileChanged.Type.Deleted || type == ProfileChanged.Type.TemporaryProfileDeleted || - type == ProfileChanged.Type.LimitLookupToOwnedChanged) + type == ProfileChanged.Type.LimitLookupToOwnedChanged || + type == ProfileChanged.Type.ApplyToCurrentlyActiveCharacterChanged) { if (profile.Armatures.Count == 0) return; @@ -534,7 +536,7 @@ public unsafe sealed class ArmatureManager : IDisposable armature.IsPendingProfileRebind = true; } - _logger.Debug($"ArmatureManager.OnProfileChange CCN/DEL/TPD/LLTOC, armature rebind scheduled: {type}, data payload: {arg3?.ToString()?.Incognify()}, profile: {profile.Name.Text.Incognify()}->{profile.Enabled}"); + _logger.Debug($"ArmatureManager.OnProfileChange CC/DEL/TPD/LLTOC/ATCACC, armature rebind scheduled: {type}, data payload: {arg3?.ToString()?.Incognify()}, profile: {profile.Name.Text.Incognify()}->{profile.Enabled}"); return; } @@ -558,4 +560,15 @@ public unsafe sealed class ArmatureManager : IDisposable yield return kvPair.Value; } } + + private IEnumerable GetArmaturesForCharacter(ActorIdentifier actorIdentifier) + { + foreach (var kvPair in Armatures) + { + (var armatureActorIdentifier, _) = _gameObjectService.GetTrueActorForSpecialTypeActor(kvPair.Key); + + if (armatureActorIdentifier.IsValid && armatureActorIdentifier.Matches(armatureActorIdentifier)) + yield return kvPair.Value; + } + } } \ No newline at end of file diff --git a/CustomizePlus/Core/ServiceManagerBuilder.cs b/CustomizePlus/Core/ServiceManagerBuilder.cs index cb45dd4..90440d5 100644 --- a/CustomizePlus/Core/ServiceManagerBuilder.cs +++ b/CustomizePlus/Core/ServiceManagerBuilder.cs @@ -206,7 +206,8 @@ public static class ServiceManagerBuilder .AddSingleton() .AddSingleton() .AddSingleton(p => new CutsceneResolver(idx => (short)p.GetRequiredService().GetParentIndex(idx))) - .AddSingleton(); + .AddSingleton() + .AddSingleton< DictBNpcENpc>(); return services; } diff --git a/CustomizePlus/Game/Services/GameObjectService.cs b/CustomizePlus/Game/Services/GameObjectService.cs index 6fac33c..46fdd22 100644 --- a/CustomizePlus/Game/Services/GameObjectService.cs +++ b/CustomizePlus/Game/Services/GameObjectService.cs @@ -31,6 +31,11 @@ public class GameObjectService _configuration = configuration; } + public ActorIdentifier GetCurrentPlayerActorIdentifier() + { + return _objectManager.PlayerData.Identifier; + } + public string GetCurrentPlayerName() { return _objectManager.PlayerData.Identifier.ToName(); diff --git a/CustomizePlus/Profiles/Data/Profile.cs b/CustomizePlus/Profiles/Data/Profile.cs index bb97d2c..faf755d 100644 --- a/CustomizePlus/Profiles/Data/Profile.cs +++ b/CustomizePlus/Profiles/Data/Profile.cs @@ -21,7 +21,7 @@ namespace CustomizePlus.Profiles.Data; /// public sealed class Profile : ISavable { - public const int Version = 4; + public const int Version = 5; private static int _nextGlobalId; @@ -29,10 +29,11 @@ public sealed class Profile : ISavable public List Armatures = new(); - [Obsolete("To be removed")] + [Obsolete("To be removed in the future versions")] public LowerString CharacterName { get; set; } = LowerString.Empty; public ActorIdentifier Character { get; set; } = ActorIdentifier.Invalid; + public bool ApplyToCurrentlyActiveCharacter { get; set; } public LowerString Name { get; set; } = LowerString.Empty; @@ -59,10 +60,10 @@ public sealed class Profile : ISavable /// public bool IsTemporary => ProfileType == ProfileType.Temporary; - /// + /* /// /// Identificator specifying specific actor this profile applies to, only works for temporary profiles /// - public ActorIdentifier TemporaryActor { get; set; } = ActorIdentifier.Invalid; + public ActorIdentifier TemporaryActor { get; set; } = ActorIdentifier.Invalid;*/ public string Incognito => UniqueId.ToString()[..8]; @@ -78,8 +79,9 @@ public sealed class Profile : ISavable /// public Profile(Profile original) : this() { - CharacterName = original.CharacterName; + Character = original.Character; LimitLookupToOwnedObjects = original.LimitLookupToOwnedObjects; + ApplyToCurrentlyActiveCharacter = original.ApplyToCurrentlyActiveCharacter; foreach (var template in original.Templates) { @@ -89,7 +91,7 @@ public sealed class Profile : ISavable public override string ToString() { - return $"Profile '{Name.Text.Incognify()}' on {CharacterName.Text.Incognify()} [{UniqueId}]"; + return $"Profile '{Name.Text.Incognify()}' on {Character.Incognito(null)} [{UniqueId}]"; } #region Serialization @@ -103,7 +105,8 @@ public sealed class Profile : ISavable ["CreationDate"] = CreationDate, ["ModifiedDate"] = ModifiedDate, ["CharacterName"] = CharacterName.Text, - //["Character"] = Character.ToJson(), + ["Character"] = Character.ToJson(), + ["ApplyToCurrentlyActiveCharacter"] = ApplyToCurrentlyActiveCharacter, ["Name"] = Name.Text, ["LimitLookupToOwnedObjects"] = LimitLookupToOwnedObjects, ["Enabled"] = Enabled, diff --git a/CustomizePlus/Profiles/Events/ProfileChanged.cs b/CustomizePlus/Profiles/Events/ProfileChanged.cs index f8c1b81..1869311 100644 --- a/CustomizePlus/Profiles/Events/ProfileChanged.cs +++ b/CustomizePlus/Profiles/Events/ProfileChanged.cs @@ -15,7 +15,7 @@ public sealed class ProfileChanged() : EventWrapper LoadV4(obj), - // 5 => LoadV5(obj), - 4 => LoadV5(obj), + 4 => LoadV4(obj), + 5 => LoadV5(obj), _ => throw new Exception("The profile to be loaded has no valid Version."), }; } private Profile LoadV4(JObject obj) { - var characterName = new LowerString(obj["CharacterName"]?.ToObject()?.Trim() ?? throw new ArgumentNullException("CharacterName")); + var profile = LoadProfileV4V5(obj); - ByteString.FromString(characterName, out var nameByteString); - var character = _actorManager.CreatePlayer(nameByteString, WorldId.AnyWorld); //todo: detect type - - obj["Character"] = character.ToJson(); - - var profile = LoadV5(obj); - - profile.ModifiedDate = DateTimeOffset.UtcNow; - _saveService.ImmediateSave(profile); + profile.CharacterName = new LowerString(obj["CharacterName"]?.ToObject()?.Trim() ?? throw new ArgumentNullException("CharacterName")); return profile; } private Profile LoadV5(JObject obj) + { + var profile = LoadProfileV4V5(obj); + + var character = _actorManager.FromJson(obj["Character"] as JObject); + + profile.Character = character; + profile.ApplyToCurrentlyActiveCharacter = obj["ApplyToCurrentlyActiveCharacter"]?.ToObject() ?? false; + profile.CharacterName = new LowerString(obj["CharacterName"]?.ToObject()?.Trim() ?? throw new ArgumentNullException("CharacterName")); //temp + + return profile; + } + + //V4 and V5 are mostly not different, so common loading logic is here + private Profile LoadProfileV4V5(JObject obj) { var creationDate = obj["CreationDate"]?.ToObject() ?? throw new ArgumentNullException("CreationDate"); - /*var character = _actorManager.FromJson(obj["Character"] as JObject); - - if (!character.IsValid) - throw new ArgumentException("Character");*/ - var profile = new Profile() { CreationDate = creationDate, UniqueId = obj["UniqueId"]?.ToObject() ?? throw new ArgumentNullException("UniqueId"), Name = new LowerString(obj["Name"]?.ToObject()?.Trim() ?? throw new ArgumentNullException("Name")), - //Character = character, - CharacterName = new LowerString(obj["CharacterName"]?.ToObject()?.Trim() ?? throw new ArgumentNullException("CharacterName")), LimitLookupToOwnedObjects = obj["LimitLookupToOwnedObjects"]?.ToObject() ?? throw new ArgumentNullException("LimitLookupToOwnedObjects"), Enabled = obj["Enabled"]?.ToObject() ?? throw new ArgumentNullException("Enabled"), ModifiedDate = obj["ModifiedDate"]?.ToObject() ?? creationDate, diff --git a/CustomizePlus/Profiles/ProfileManager.cs b/CustomizePlus/Profiles/ProfileManager.cs index 2413293..19c41df 100644 --- a/CustomizePlus/Profiles/ProfileManager.cs +++ b/CustomizePlus/Profiles/ProfileManager.cs @@ -168,26 +168,24 @@ public partial class ProfileManager : IDisposable } /// - /// Change character name for profile + /// Change character associated with profile /// - public void ChangeCharacterName(Profile profile, string newName) + public void ChangeCharacter(Profile profile, ActorIdentifier actorIdentifier) { - newName = newName.Trim(); - - var oldName = profile.CharacterName.Text; - if (oldName == newName) + if (!actorIdentifier.IsValid || actorIdentifier.Matches(profile.Character)) return; - profile.CharacterName = newName; + var oldCharacter = profile.Character; + profile.Character = actorIdentifier; //Called so all other active profiles for new character name get disabled //saving is performed there - SetEnabled(profile, profile.Enabled, true); + //SetEnabled(profile, profile.Enabled, true); //todo SaveProfile(profile); - _logger.Debug($"Changed character name for profile {profile.UniqueId}."); - _event.Invoke(ProfileChanged.Type.ChangedCharacterName, profile, oldName); + _logger.Debug($"Changed character for profile {profile.UniqueId}."); + _event.Invoke(ProfileChanged.Type.ChangedCharacter, profile, oldCharacter); } /// @@ -229,7 +227,7 @@ public partial class ProfileManager : IDisposable _logger.Debug($"Setting {profile} as enabled..."); foreach (var otherProfile in Profiles - .Where(x => x.CharacterName == profile.CharacterName && x != profile && x.Enabled && !x.IsTemporary)) + .Where(x => x.Character.Matches(profile.Character) && x != profile && x.Enabled && !x.IsTemporary)) { _logger.Debug($"\t-> {otherProfile} disabled"); SetEnabled(otherProfile, false); @@ -268,6 +266,18 @@ public partial class ProfileManager : IDisposable } } + public void SetApplyToCurrentlyActiveCharacter(Profile profile, bool value) + { + if (profile.ApplyToCurrentlyActiveCharacter != value) + { + profile.ApplyToCurrentlyActiveCharacter = value; + + SaveProfile(profile); + + _event.Invoke(ProfileChanged.Type.ApplyToCurrentlyActiveCharacterChanged, profile, value); + } + } + public void DeleteTemplate(Profile profile, int templateIndex) { _logger.Debug($"Deleting template #{templateIndex} from {profile}..."); @@ -342,6 +352,7 @@ public partial class ProfileManager : IDisposable _event.Invoke(ProfileChanged.Type.ChangedDefaultProfile, profile, previousProfile); } + //warn: temporary profile system does not support any world identifiers public void AddTemporaryProfile(Profile profile, Actor actor/*, Template template*/) { if (!actor.Identifier(_actorManager, out var identifier)) @@ -349,14 +360,13 @@ public partial class ProfileManager : IDisposable profile.Enabled = true; profile.ProfileType = ProfileType.Temporary; - profile.TemporaryActor = identifier; - profile.CharacterName = identifier.ToNameWithoutOwnerName(); + profile.Character = identifier; profile.LimitLookupToOwnedObjects = false; - var existingProfile = Profiles.FirstOrDefault(x => x.CharacterName.Lower == profile.CharacterName.Lower && x.IsTemporary); + var existingProfile = Profiles.FirstOrDefault(x => x.Character.Matches(profile.Character) && x.IsTemporary); if (existingProfile != null) { - _logger.Debug($"Temporary profile for {existingProfile.CharacterName} already exists, removing..."); + _logger.Debug($"Temporary profile for {existingProfile.Character.Incognito(null)} already exists, removing..."); Profiles.Remove(existingProfile); _event.Invoke(ProfileChanged.Type.TemporaryProfileDeleted, existingProfile, null); } @@ -375,7 +385,7 @@ public partial class ProfileManager : IDisposable if (!Profiles.Remove(profile)) throw new ProfileNotFoundException(); - _logger.Debug($"Removed temporary profile for {profile.CharacterName}"); + _logger.Debug($"Removed temporary profile for {profile.Character.Incognito(null)}"); _event.Invoke(ProfileChanged.Type.TemporaryProfileDeleted, profile, null); } @@ -394,7 +404,7 @@ public partial class ProfileManager : IDisposable if (!actor.Identifier(_actorManager, out var identifier)) throw new ActorNotFoundException(); - var profile = Profiles.FirstOrDefault(x => x.TemporaryActor == identifier && x.IsTemporary); + var profile = Profiles.FirstOrDefault(x => x.Character == identifier && x.IsTemporary); if (profile == null) throw new ProfileNotFoundException(); @@ -402,17 +412,16 @@ public partial class ProfileManager : IDisposable } /// - /// Return profile by character name, does not return temporary profiles + /// Return profile by actor identifier, does not return temporary profiles. /// - /// - /// - /// - public Profile? GetProfileByCharacterName(string name, bool enabledOnly = false) + public Profile? GetProfileByActor(Actor actor, bool enabledOnly = false) { - if (string.IsNullOrWhiteSpace(name)) + var actorIdentifier = actor.GetIdentifier(_actorManager); + + if (!actorIdentifier.IsValid) return null; - var query = Profiles.Where(x => x.CharacterName == name); + var query = Profiles.Where(x => x.Character.Matches(actorIdentifier) && !x.IsTemporary); if (enabledOnly) query = query.Where(x => x.Enabled); @@ -447,7 +456,16 @@ public partial class ProfileManager : IDisposable if (profile == DefaultProfile) return false; - return profile.CharacterName.Text == name && + if (profile.ApplyToCurrentlyActiveCharacter) + { + if (_objectManager.IsInLobby) + return true; + + var currentPlayer = _actorManager.GetCurrentPlayer(); + return currentPlayer.IsValid && _actorManager.GetCurrentPlayer().Matches(actorIdentifier); + } + + return (profile.CharacterName.Text == name || profile.Character.Matches(actorIdentifier)) && (!profile.LimitLookupToOwnedObjects || (actorIdentifier.Type == IdentifierType.Owned && actorIdentifier.PlayerName == _actorManager.GetCurrentPlayer().PlayerName)); @@ -458,8 +476,19 @@ public partial class ProfileManager : IDisposable foreach (var profile in Profiles) { - if (IsProfileAppliesToCurrentActor(profile) && profile.Enabled) - yield return profile; + if(IsProfileAppliesToCurrentActor(profile)) + { + //todo: temp for migrations to v5 + if (!profile.Character.IsValid) + { + _logger.Warning($"No character for profile {profile}, but character has been found as: {actorIdentifier}, will set."); + profile.Character = actorIdentifier; + _saveService.QueueSave(profile); + } + + if (profile.Enabled) + yield return profile; + } } if (DefaultProfile != null && @@ -553,7 +582,7 @@ public partial class ProfileManager : IDisposable if (!Profiles.Remove(profile)) return; - _logger.Debug($"ProfileManager.OnArmatureChange: Removed unused temporary profile for {profile.CharacterName}"); + _logger.Debug($"ProfileManager.OnArmatureChange: Removed unused temporary profile for {profile.Character.Incognito(null)}"); _event.Invoke(ProfileChanged.Type.TemporaryProfileDeleted, profile, null); } diff --git a/CustomizePlus/UI/Windows/Controls/ActorAssignmentUi.cs b/CustomizePlus/UI/Windows/Controls/ActorAssignmentUi.cs index e3a2a78..0003320 100644 --- a/CustomizePlus/UI/Windows/Controls/ActorAssignmentUi.cs +++ b/CustomizePlus/UI/Windows/Controls/ActorAssignmentUi.cs @@ -1,39 +1,67 @@ -using Dalamud.Game.ClientState.Objects.Enums; -using ImGuiNET; -using OtterGui.Custom; -using Penumbra.GameData.Actors; -using Penumbra.GameData.Enums; -using Penumbra.GameData.Gui; -using Penumbra.GameData.Interop; -using System; +using System; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; using System.Threading.Tasks; +using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Plugin; +using Dalamud.Plugin.Services; +using ImGuiNET; +using Lumina.Excel.GeneratedSheets; +using OtterGui.Custom; +using OtterGui.Log; +using Penumbra.GameData.Actors; +using Penumbra.GameData.Data; +using Penumbra.GameData.DataContainers.Bases; +using Penumbra.GameData.Gui; +using Penumbra.GameData.Structs; +using Penumbra.String; namespace CustomizePlus.UI.Windows.Controls; -public class ActorAssignmentUi : IDisposable +public class ActorAssignmentUi { private readonly ActorManager _actorManager; + private readonly DictBNpcENpc _dictBnpcEnpc; private WorldCombo _worldCombo = null!; - private NpcCombo _mountCombo = null!; - private NpcCombo _companionCombo = null!; - private NpcCombo _ornamentCombo = null!; - private NpcCombo _bnpcCombo = null!; - private NpcCombo _enpcCombo = null!; + private Penumbra.GameData.Gui.NpcCombo _mountCombo = null!; + private Penumbra.GameData.Gui.NpcCombo _companionCombo = null!; + //private BattleEventNpcCombo _npcCombo = null!; + private Penumbra.GameData.Gui.NpcCombo _npcCombo = null!; private bool _ready; private string _newCharacterName = string.Empty; private ObjectKind _newKind = ObjectKind.BattleNpc; - public ActorAssignmentUi(ActorManager actorManager) + /* public string CharacterName { get => _newCharacterName; } + public WorldId SelectedWorld { get => _worldCombo.CurrentSelection.Key; } + */ + public ActorIdentifier NpcIdentifier { get; private set; } = ActorIdentifier.Invalid; + public ActorIdentifier PlayerIdentifier { get; private set; } = ActorIdentifier.Invalid; + public ActorIdentifier RetainerIdentifier { get; private set; } = ActorIdentifier.Invalid; + public ActorIdentifier MannequinIdentifier { get; private set; } = ActorIdentifier.Invalid; + + public bool CanSetPlayer + => PlayerIdentifier.IsValid; + + public bool CanSetRetainer + => RetainerIdentifier.IsValid; + + public bool CanSetMannequin + => MannequinIdentifier.IsValid; + + public bool CanSetNpc + => NpcIdentifier.IsValid; + + public ActorAssignmentUi(ActorManager actorManager, DictBNpcENpc dictBnpcEnpc) { _actorManager = actorManager; + _dictBnpcEnpc = dictBnpcEnpc; - _actorManager.Awaiter.ContinueWith(_ => SetupCombos(), TaskScheduler.Default); + _actorManager.Awaiter.ContinueWith(_ => dictBnpcEnpc.Awaiter.ContinueWith(_ => SetupCombos(), TaskScheduler.Default), TaskScheduler.Default); } public void DrawWorldCombo(float width) @@ -64,6 +92,12 @@ public class ActorAssignmentUi : IDisposable if (!_ready) return; + /* if(_newKind == ObjectKind.BattleNpc || _newKind == ObjectKind.EventNpc) + { + if (_npcCombo.Draw(width)) + UpdateIdentifiersInternal(); + }*/ + var combo = GetNpcCombo(_newKind); if (combo.Draw(width)) UpdateIdentifiersInternal(); @@ -75,17 +109,15 @@ public class ActorAssignmentUi : IDisposable ObjectKind.EventNpc, ObjectKind.Companion, ObjectKind.MountType, - ObjectKind.Ornament, }; - private NpcCombo GetNpcCombo(ObjectKind kind) + private Penumbra.GameData.Gui.NpcCombo GetNpcCombo(ObjectKind kind) => kind switch { - ObjectKind.BattleNpc => _bnpcCombo, - ObjectKind.EventNpc => _enpcCombo, + ObjectKind.BattleNpc => _npcCombo, + ObjectKind.EventNpc => _npcCombo, ObjectKind.MountType => _mountCombo, ObjectKind.Companion => _companionCombo, - ObjectKind.Ornament => _ornamentCombo, _ => throw new NotImplementedException(), }; @@ -93,63 +125,57 @@ public class ActorAssignmentUi : IDisposable private void SetupCombos() { _worldCombo = new WorldCombo(_actorManager.Data.Worlds, Plugin.Logger); - _mountCombo = new NpcCombo("##mountCombo", _actorManager.Data.Mounts, Plugin.Logger); - _companionCombo = new NpcCombo("##companionCombo", _actorManager.Data.Companions, Plugin.Logger); - _ornamentCombo = new NpcCombo("##ornamentCombo", _actorManager.Data.Ornaments, Plugin.Logger); - _bnpcCombo = new NpcCombo("##bnpcCombo", _actorManager.Data.BNpcs, Plugin.Logger); - _enpcCombo = new NpcCombo("##enpcCombo", _actorManager.Data.ENpcs, Plugin.Logger); + _mountCombo = new Penumbra.GameData.Gui.NpcCombo("##mountCombo", _actorManager.Data.Mounts, Plugin.Logger); + _companionCombo = new Penumbra.GameData.Gui.NpcCombo("##companionCombo", _actorManager.Data.Companions, Plugin.Logger); + //_bnpcCombo = new Penumbra.GameData.Gui.NpcCombo("##bnpcCombo", _actorManager.Data.BNpcs, Plugin.Logger); + //_enpcCombo = new Penumbra.GameData.Gui.NpcCombo("##enpcCombo", _actorManager.Data.ENpcs, Plugin.Logger); + _npcCombo = new Penumbra.GameData.Gui.NpcCombo("##npcCombo", _dictBnpcEnpc, Plugin.Logger); _ready = true; } private void UpdateIdentifiersInternal() { - /* var combo = GetNpcCombo(_newKind); - PlayerTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Player, _newCharacterName, - _worldCombo.CurrentSelection.Key, ObjectKind.None, [], out _playerIdentifiers) switch + if (ByteString.FromString(_newCharacterName, out var byteName)) { - _ when _newCharacterName.Length == 0 => NewPlayerTooltipEmpty, - IndividualCollections.AddResult.Invalid => NewPlayerTooltipInvalid, - IndividualCollections.AddResult.AlreadySet => AlreadyAssigned, - _ => string.Empty, - }; - RetainerTooltip = - _collectionManager.Active.Individuals.CanAdd(IdentifierType.Retainer, _newCharacterName, 0, ObjectKind.None, [], - out _retainerIdentifiers) switch - { - _ when _newCharacterName.Length == 0 => NewRetainerTooltipEmpty, - IndividualCollections.AddResult.Invalid => NewRetainerTooltipInvalid, - IndividualCollections.AddResult.AlreadySet => AlreadyAssigned, - _ => string.Empty, - }; - if (combo.CurrentSelection.Ids != null) - { - NpcTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Npc, string.Empty, ushort.MaxValue, _newKind, - combo.CurrentSelection.Ids, out _npcIdentifiers) switch - { - IndividualCollections.AddResult.AlreadySet => AlreadyAssigned, - _ => string.Empty, - }; - OwnedTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Owned, _newCharacterName, - _worldCombo.CurrentSelection.Key, _newKind, - combo.CurrentSelection.Ids, out _ownedIdentifiers) switch - { - _ when _newCharacterName.Length == 0 => NewPlayerTooltipEmpty, - IndividualCollections.AddResult.Invalid => NewPlayerTooltipInvalid, - IndividualCollections.AddResult.AlreadySet => AlreadyAssigned, - _ => string.Empty, - }; + PlayerIdentifier = _actorManager.CreatePlayer(byteName, _worldCombo.CurrentSelection.Key); + RetainerIdentifier = _actorManager.CreateRetainer(byteName, ActorIdentifier.RetainerType.Bell); + MannequinIdentifier = _actorManager.CreateRetainer(byteName, ActorIdentifier.RetainerType.Mannequin); } - else - { - NpcTooltip = NewNpcTooltipEmpty; - OwnedTooltip = NewNpcTooltipEmpty; - _npcIdentifiers = []; - _ownedIdentifiers = []; - }*/ - } - - public void Dispose() - { - //throw new NotImplementedException(); } } + +//Todo: Temp +/// A dictionary that matches BNpcNameId to names. +public sealed class DictBNpcENpc(IDalamudPluginInterface pluginInterface, Logger log, IDataManager gameData) + : NameDictionary(pluginInterface, log, gameData, "BNpcsENpcs", 7, () => CreateData(gameData)) +{ + /// Create the data. + private static IReadOnlyDictionary CreateData(IDataManager gameData) + { + + var sheet = gameData.GetExcelSheet(gameData.Language)!; + var sheet2 = gameData.GetExcelSheet(gameData.Language)!; + + var dict = new Dictionary((int)sheet.RowCount + (int)sheet2.RowCount); + + foreach (var n in sheet.Where(n => n.Singular.RawData.Length > 0)) + dict.TryAdd(n.RowId, DataUtility.ToTitleCaseExtended(n.Singular, n.Article)); + foreach (var n in sheet2.Where(e => e.Singular.RawData.Length > 0)) + dict.TryAdd(n.RowId, DataUtility.ToTitleCaseExtended(n.Singular, n.Article)); + + return dict.ToFrozenDictionary(); + } + + /// + public bool ContainsKey(BNpcNameId key) + => Value.ContainsKey(key.Id); + + /// + public bool TryGetValue(BNpcNameId key, [NotNullWhen(true)] out string? value) + => Value.TryGetValue(key.Id, out value); + + /// + public string this[BNpcNameId key] + => Value[key.Id]; +} + diff --git a/CustomizePlus/UI/Windows/MainWindow/Tabs/Profiles/ProfileFileSystemSelector.cs b/CustomizePlus/UI/Windows/MainWindow/Tabs/Profiles/ProfileFileSystemSelector.cs index 90ae124..9f28bdd 100644 --- a/CustomizePlus/UI/Windows/MainWindow/Tabs/Profiles/ProfileFileSystemSelector.cs +++ b/CustomizePlus/UI/Windows/MainWindow/Tabs/Profiles/ProfileFileSystemSelector.cs @@ -132,7 +132,7 @@ public class ProfileFileSystemSelector : FileSystemSelector