diff --git a/Penumbra.GameData/Data/EquipmentIdentificationList.cs b/Penumbra.GameData/Data/EquipmentIdentificationList.cs index 5ce5e521..25e2e095 100644 --- a/Penumbra.GameData/Data/EquipmentIdentificationList.cs +++ b/Penumbra.GameData/Data/EquipmentIdentificationList.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using System.Linq; using Dalamud; -using Dalamud.Data; using Dalamud.Plugin; -using Lumina.Excel.GeneratedSheets; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using PseudoEquipItem = System.ValueTuple; @@ -14,8 +12,8 @@ internal sealed class EquipmentIdentificationList : KeyList { private const string Tag = "EquipmentIdentification"; - public EquipmentIdentificationList(DalamudPluginInterface pi, ClientLanguage language, DataManager gameData) - : base(pi, Tag, language, ObjectIdentification.IdentificationVersion, CreateEquipmentList(gameData, language)) + public EquipmentIdentificationList(DalamudPluginInterface pi, ClientLanguage language, ItemData data) + : base(pi, Tag, language, ObjectIdentification.IdentificationVersion, CreateEquipmentList(data)) { } public IEnumerable Between(SetId modelId, EquipSlot slot = EquipSlot.Unknown, byte variant = 0) @@ -48,7 +46,7 @@ internal sealed class EquipmentIdentificationList : KeyList protected override int ValueKeySelector(PseudoEquipItem data) => (int)data.Item2; - private static IEnumerable CreateEquipmentList(DataManager gameData, ClientLanguage language) + private static IEnumerable CreateEquipmentList(ItemData data) { var items = gameData.GetExcelSheet(language)!; return items.Where(i => ((EquipSlot)i.EquipSlotCategory.Row).IsEquipmentPiece()) @@ -59,6 +57,13 @@ internal sealed class EquipmentIdentificationList : KeyList private static IEnumerable CustomList => new[] { - (PseudoEquipItem)EquipItem.FromIds(0, 0, 8100, 0, 1, FullEquipType.Body, "Reaper Shroud"), + (PseudoEquipItem)EquipItem.FromIds(0, 0, 8100, 0, 1, FullEquipType.Body, "Reaper Shroud"), + (PseudoEquipItem)EquipItem.FromIds(0, 0, 9041, 0, 1, FullEquipType.Head, "Cid's Bandana (9041)"), + (PseudoEquipItem)EquipItem.FromIds(0, 0, 9041, 0, 1, FullEquipType.Body, "Cid's Body (9041)"), + (PseudoEquipItem)EquipItem.FromIds(0, 0, 9903, 0, 1, FullEquipType.Head, "Smallclothes (NPC, 9903)"), + (PseudoEquipItem)EquipItem.FromIds(0, 0, 9903, 0, 1, FullEquipType.Body, "Smallclothes (NPC, 9903)"), + (PseudoEquipItem)EquipItem.FromIds(0, 0, 9903, 0, 1, FullEquipType.Hands, "Smallclothes (NPC, 9903)"), + (PseudoEquipItem)EquipItem.FromIds(0, 0, 9903, 0, 1, FullEquipType.Legs, "Smallclothes (NPC, 9903)"), + (PseudoEquipItem)EquipItem.FromIds(0, 0, 9903, 0, 1, FullEquipType.Feet, "Smallclothes (NPC, 9903)"), }; } diff --git a/Penumbra.GameData/Data/ItemData.cs b/Penumbra.GameData/Data/ItemData.cs index cece8732..c9f69694 100644 --- a/Penumbra.GameData/Data/ItemData.cs +++ b/Penumbra.GameData/Data/ItemData.cs @@ -16,6 +16,7 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary _mainItems; private readonly IReadOnlyDictionary _offItems; + private readonly IReadOnlyDictionary _gauntlets; private readonly IReadOnlyList> _byType; private static IReadOnlyList> CreateItems(DataManager dataManager, ClientLanguage language) @@ -28,10 +29,23 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary> 16), FullEquipType.Hands)); + tmp[(int)FullEquipType.FistsOff].Add(new EquipItem(mh.Name + FullEquipType.FistsOff.OffhandTypeSuffix(), mh.Id, + mh.IconId, (SetId)(mh.ModelId.Value + 50), mh.WeaponType, mh.Variant, FullEquipType.FistsOff)); + } + else + { + tmp[(int)type.ValidOffhand()].Add(EquipItem.FromOffhand(item)); + } + } } else if (type != FullEquipType.Unknown) { @@ -47,18 +61,26 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary CreateMainItems(IReadOnlyList> items) + private static Tuple, IReadOnlyDictionary> CreateMainItems( + IReadOnlyList> items) { var dict = new Dictionary(1024 * 4); + foreach (var fistWeapon in items[(int)FullEquipType.Fists]) + dict.TryAdd((uint)fistWeapon.Item2, fistWeapon); + + var gauntlets = items[(int)FullEquipType.Hands].Where(g => dict.ContainsKey((uint)g.Item2)).ToDictionary(g => (uint)g.Item2, g => g); + gauntlets.TrimExcess(); + foreach (var type in Enum.GetValues().Where(v => !FullEquipTypeExtensions.OffhandTypes.Contains(v))) { var list = items[(int)type]; foreach (var item in list) - dict.TryAdd((uint) item.Item2, item); + dict.TryAdd((uint)item.Item2, item); } dict.TrimExcess(); - return dict; + return new Tuple, + IReadOnlyDictionary>(dict, gauntlets); } private static IReadOnlyDictionary CreateOffItems(IReadOnlyList> items) @@ -68,7 +90,7 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary CreateItems(dataManager, language)); - _mainItems = TryCatchData("ItemDictMain", () => CreateMainItems(_byType)); - _offItems = TryCatchData("ItemDictOff", () => CreateOffItems(_byType)); + _byType = TryCatchData("ItemList", () => CreateItems(dataManager, language)); + (_mainItems, _gauntlets) = TryCatchData("ItemDictMain", () => CreateMainItems(_byType)); + _offItems = TryCatchData("ItemDictOff", () => CreateOffItems(_byType)); } protected override void DisposeInternal() @@ -120,31 +142,16 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary this[FullEquipType key] => TryGetValue(key, out var ret) ? ret : throw new IndexOutOfRangeException(); - public bool ContainsKey(uint key, bool main = true) - => main ? _mainItems.ContainsKey(key) : _offItems.ContainsKey(key); - - public bool TryGetValue(uint key, out EquipItem value) - { - if (_mainItems.TryGetValue(key, out var v)) - { - value = v; - return true; - } - - value = default; - return false; - } - public IEnumerable<(uint, EquipItem)> AllItems(bool main) => (main ? _mainItems : _offItems).Select(i => (i.Key, (EquipItem)i.Value)); public int TotalItemCount(bool main) => main ? _mainItems.Count : _offItems.Count; - public bool TryGetValue(uint key, bool main, out EquipItem value) + public bool TryGetValue(uint key, EquipSlot slot, out EquipItem value) { - var dict = main ? _mainItems : _offItems; - if (dict.TryGetValue(key, out var v)) + var dict = slot is EquipSlot.OffHand ? _offItems : _mainItems; + if (slot is EquipSlot.Hands && _gauntlets.TryGetValue(key, out var v) || dict.TryGetValue(key, out v)) { value = v; return true; diff --git a/Penumbra.GameData/Data/ObjectIdentification.cs b/Penumbra.GameData/Data/ObjectIdentification.cs index 25b01f81..4aa766a1 100644 --- a/Penumbra.GameData/Data/ObjectIdentification.cs +++ b/Penumbra.GameData/Data/ObjectIdentification.cs @@ -20,7 +20,7 @@ namespace Penumbra.GameData.Data; internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier { - public const int IdentificationVersion = 2; + public const int IdentificationVersion = 3; public IGamePathParser GamePathParser { get; } = new GamePathParser(); public readonly IReadOnlyList> BnpcNames; @@ -32,12 +32,12 @@ internal sealed class ObjectIdentification : DataSharer, IObjectIdentifier private readonly WeaponIdentificationList _weapons; private readonly ModelIdentificationList _modelIdentifierToModelChara; - public ObjectIdentification(DalamudPluginInterface pluginInterface, DataManager dataManager, ClientLanguage language) + public ObjectIdentification(DalamudPluginInterface pluginInterface, DataManager dataManager, ItemData itemData, ClientLanguage language) : base(pluginInterface, language, IdentificationVersion) { _actorData = new ActorManager.ActorManagerData(pluginInterface, dataManager, language); - _equipment = new EquipmentIdentificationList(pluginInterface, language, dataManager); - _weapons = new WeaponIdentificationList(pluginInterface, language, dataManager); + _equipment = new EquipmentIdentificationList(pluginInterface, language, itemData); + _weapons = new WeaponIdentificationList(pluginInterface, language, itemData); Actions = TryCatchData("Actions", () => CreateActionList(dataManager)); _modelIdentifierToModelChara = new ModelIdentificationList(pluginInterface, language, dataManager); diff --git a/Penumbra.GameData/Data/WeaponIdentificationList.cs b/Penumbra.GameData/Data/WeaponIdentificationList.cs index 90ef46c7..e4566769 100644 --- a/Penumbra.GameData/Data/WeaponIdentificationList.cs +++ b/Penumbra.GameData/Data/WeaponIdentificationList.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using System.Linq; using Dalamud; -using Dalamud.Data; using Dalamud.Plugin; -using Lumina.Excel.GeneratedSheets; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using PseudoEquipItem = System.ValueTuple; @@ -13,10 +11,10 @@ namespace Penumbra.GameData.Data; internal sealed class WeaponIdentificationList : KeyList { private const string Tag = "WeaponIdentification"; - private const int Version = 1; + private const int Version = 2; - public WeaponIdentificationList(DalamudPluginInterface pi, ClientLanguage language, DataManager gameData) - : base(pi, Tag, language, Version, CreateWeaponList(gameData, language)) + public WeaponIdentificationList(DalamudPluginInterface pi, ClientLanguage language, ItemData data) + : base(pi, Tag, language, Version, CreateWeaponList(data)) { } public IEnumerable Between(SetId modelId) @@ -52,17 +50,8 @@ internal sealed class WeaponIdentificationList : KeyList protected override int ValueKeySelector(PseudoEquipItem data) => (int)data.Item2; - private static IEnumerable CreateWeaponList(DataManager gameData, ClientLanguage language) - => gameData.GetExcelSheet(language)!.SelectMany(ToEquipItems); - - private static IEnumerable ToEquipItems(Item item) - { - if ((EquipSlot)item.EquipSlotCategory.Row is not (EquipSlot.MainHand or EquipSlot.OffHand or EquipSlot.BothHand)) - yield break; - - if (item.ModelMain != 0) - yield return (PseudoEquipItem)EquipItem.FromMainhand(item); - if (item.ModelSub != 0) - yield return (PseudoEquipItem)EquipItem.FromOffhand(item); - } + private static IEnumerable CreateWeaponList(ItemData data) + => data.Where(kvp => !kvp.Key.IsEquipment() && !kvp.Key.IsAccessory()) + .SelectMany(kvp => kvp.Value) + .Select(i => (PseudoEquipItem)i); } diff --git a/Penumbra.GameData/GameData.cs b/Penumbra.GameData/GameData.cs index 81657bff..5f8c2fe4 100644 --- a/Penumbra.GameData/GameData.cs +++ b/Penumbra.GameData/GameData.cs @@ -15,14 +15,14 @@ public static class GameData /// /// Obtain an object identifier that can link a game path to game objects that use it, using your client language. /// - public static IObjectIdentifier GetIdentifier(DalamudPluginInterface pluginInterface, DataManager dataManager) - => new ObjectIdentification(pluginInterface, dataManager, dataManager.Language); + public static IObjectIdentifier GetIdentifier(DalamudPluginInterface pluginInterface, DataManager dataManager, ItemData itemData) + => new ObjectIdentification(pluginInterface, dataManager, itemData, dataManager.Language); /// /// Obtain an object identifier that can link a game path to game objects that use it using the given language. /// - public static IObjectIdentifier GetIdentifier(DalamudPluginInterface pluginInterface, DataManager dataManager, ClientLanguage language) - => new ObjectIdentification(pluginInterface, dataManager, language); + public static IObjectIdentifier GetIdentifier(DalamudPluginInterface pluginInterface, DataManager dataManager, ItemData itemData, ClientLanguage language) + => new ObjectIdentification(pluginInterface, dataManager, itemData, language); /// /// Obtain a parser for game paths.