Make equipitems sharable again.

This commit is contained in:
Ottermandias 2023-06-13 21:04:19 +02:00
parent 323b4d6f21
commit 8436455936
9 changed files with 146 additions and 52 deletions

View file

@ -6,10 +6,11 @@ using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
namespace Penumbra.GameData.Data; namespace Penumbra.GameData.Data;
internal sealed class EquipmentIdentificationList : KeyList<EquipItem> internal sealed class EquipmentIdentificationList : KeyList<PseudoEquipItem>
{ {
private const string Tag = "EquipmentIdentification"; private const string Tag = "EquipmentIdentification";
@ -20,11 +21,11 @@ internal sealed class EquipmentIdentificationList : KeyList<EquipItem>
public IEnumerable<EquipItem> Between(SetId modelId, EquipSlot slot = EquipSlot.Unknown, byte variant = 0) public IEnumerable<EquipItem> Between(SetId modelId, EquipSlot slot = EquipSlot.Unknown, byte variant = 0)
{ {
if (slot == EquipSlot.Unknown) if (slot == EquipSlot.Unknown)
return Between(ToKey(modelId, 0, 0), ToKey(modelId, (EquipSlot)0xFF, 0xFF)); return Between(ToKey(modelId, 0, 0), ToKey(modelId, (EquipSlot)0xFF, 0xFF)).Select(e => (EquipItem)e);
if (variant == 0) if (variant == 0)
return Between(ToKey(modelId, slot, 0), ToKey(modelId, slot, 0xFF)); return Between(ToKey(modelId, slot, 0), ToKey(modelId, slot, 0xFF)).Select(e => (EquipItem)e);
return Between(ToKey(modelId, slot, variant), ToKey(modelId, slot, variant)); return Between(ToKey(modelId, slot, variant), ToKey(modelId, slot, variant)).Select(e => (EquipItem)e);
} }
public void Dispose(DalamudPluginInterface pi, ClientLanguage language) public void Dispose(DalamudPluginInterface pi, ClientLanguage language)
@ -34,9 +35,9 @@ internal sealed class EquipmentIdentificationList : KeyList<EquipItem>
=> ((ulong)modelId << 32) | ((ulong)slot << 16) | variant; => ((ulong)modelId << 32) | ((ulong)slot << 16) | variant;
public static ulong ToKey(EquipItem i) public static ulong ToKey(EquipItem i)
=> ToKey(i.ModelId, i.Slot, i.Variant); => ToKey(i.ModelId, i.Type.ToSlot(), i.Variant);
protected override IEnumerable<ulong> ToKeys(EquipItem i) protected override IEnumerable<ulong> ToKeys(PseudoEquipItem i)
{ {
yield return ToKey(i); yield return ToKey(i);
} }
@ -44,12 +45,12 @@ internal sealed class EquipmentIdentificationList : KeyList<EquipItem>
protected override bool ValidKey(ulong key) protected override bool ValidKey(ulong key)
=> key != 0; => key != 0;
protected override int ValueKeySelector(EquipItem data) protected override int ValueKeySelector(PseudoEquipItem data)
=> (int)data.Id; => (int)data.Item2;
private static IEnumerable<EquipItem> CreateEquipmentList(DataManager gameData, ClientLanguage language) private static IEnumerable<PseudoEquipItem> CreateEquipmentList(DataManager gameData, ClientLanguage language)
{ {
var items = gameData.GetExcelSheet<Item>(language)!; var items = gameData.GetExcelSheet<Item>(language)!;
return items.Where(i => ((EquipSlot)i.EquipSlotCategory.Row).IsEquipmentPiece()).Select(EquipItem.FromArmor); return items.Where(i => ((EquipSlot)i.EquipSlotCategory.Row).IsEquipmentPiece()).Select(i => (PseudoEquipItem)EquipItem.FromArmor(i));
} }
} }

View file

@ -1,5 +1,4 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
@ -18,7 +17,6 @@ public static partial class GamePaths
: GenderRace.Unknown; : GenderRace.Unknown;
} }
public static partial class Monster public static partial class Monster
{ {
public static partial class Imc public static partial class Imc

View file

@ -8,14 +8,17 @@ using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
namespace Penumbra.GameData.Data; namespace Penumbra.GameData.Data;
public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IReadOnlyList<EquipItem>> public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IReadOnlyList<EquipItem>>
{ {
private readonly IReadOnlyList<IReadOnlyList<EquipItem>> _items; private readonly IReadOnlyDictionary<uint, PseudoEquipItem> _mainItems;
private readonly IReadOnlyDictionary<uint, PseudoEquipItem> _offItems;
private readonly IReadOnlyList<IReadOnlyList<PseudoEquipItem>> _byType;
private static IReadOnlyList<IReadOnlyList<EquipItem>> CreateItems(DataManager dataManager, ClientLanguage language) private static IReadOnlyList<IReadOnlyList<PseudoEquipItem>> CreateItems(DataManager dataManager, ClientLanguage language)
{ {
var tmp = Enum.GetValues<FullEquipType>().Select(_ => new List<EquipItem>(1024)).ToArray(); var tmp = Enum.GetValues<FullEquipType>().Select(_ => new List<EquipItem>(1024)).ToArray();
@ -28,7 +31,7 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IR
if (item.ModelMain != 0) if (item.ModelMain != 0)
tmp[(int)type].Add(EquipItem.FromMainhand(item)); tmp[(int)type].Add(EquipItem.FromMainhand(item));
if (item.ModelSub != 0) if (item.ModelSub != 0)
tmp[(int)type].Add(EquipItem.FromOffhand(item)); tmp[(int)type.Offhand()].Add(EquipItem.FromOffhand(item));
} }
else if (type != FullEquipType.Unknown) else if (type != FullEquipType.Unknown)
{ {
@ -36,56 +39,141 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IR
} }
} }
var ret = new IReadOnlyList<EquipItem>[tmp.Length]; var ret = new IReadOnlyList<PseudoEquipItem>[tmp.Length];
ret[0] = Array.Empty<EquipItem>(); ret[0] = Array.Empty<PseudoEquipItem>();
for (var i = 1; i < tmp.Length; ++i) for (var i = 1; i < tmp.Length; ++i)
ret[i] = tmp[i].OrderBy(item => item.Name).ToArray(); ret[i] = tmp[i].OrderBy(item => item.Name).Select(s => (PseudoEquipItem)s).ToArray();
return ret; return ret;
} }
private static IReadOnlyDictionary<uint, PseudoEquipItem> CreateMainItems(IReadOnlyList<IReadOnlyList<PseudoEquipItem>> items)
{
var dict = new Dictionary<uint, PseudoEquipItem>(1024 * 4);
foreach (var type in Enum.GetValues<FullEquipType>().Where(v => !FullEquipTypeExtensions.OffhandTypes.Contains(v)))
{
var list = items[(int)type];
foreach (var item in list)
dict.TryAdd(item.Item2, item);
}
dict.TrimExcess();
return dict;
}
private static IReadOnlyDictionary<uint, PseudoEquipItem> CreateOffItems(IReadOnlyList<IReadOnlyList<PseudoEquipItem>> items)
{
var dict = new Dictionary<uint, PseudoEquipItem>(128);
foreach (var type in FullEquipTypeExtensions.OffhandTypes)
{
var list = items[(int)type];
foreach (var item in list)
dict.TryAdd(item.Item2, item);
}
dict.TrimExcess();
return dict;
}
public ItemData(DalamudPluginInterface pluginInterface, DataManager dataManager, ClientLanguage language) public ItemData(DalamudPluginInterface pluginInterface, DataManager dataManager, ClientLanguage language)
: base(pluginInterface, language, 1) : base(pluginInterface, language, 1)
{ {
_items = TryCatchData("ItemList", () => CreateItems(dataManager, language)); _byType = TryCatchData("ItemList", () => CreateItems(dataManager, language));
_mainItems = TryCatchData("ItemDictMain", () => CreateMainItems(_byType));
_offItems = TryCatchData("ItemDictOff", () => CreateOffItems(_byType));
} }
protected override void DisposeInternal() protected override void DisposeInternal()
=> DisposeTag("ItemList"); {
DisposeTag("ItemList");
DisposeTag("ItemDictMain");
DisposeTag("ItemDictOff");
}
public IEnumerator<KeyValuePair<FullEquipType, IReadOnlyList<EquipItem>>> GetEnumerator() public IEnumerator<KeyValuePair<FullEquipType, IReadOnlyList<EquipItem>>> GetEnumerator()
{ {
for (var i = 1; i < _items.Count; ++i) for (var i = 1; i < _byType.Count; ++i)
yield return new KeyValuePair<FullEquipType, IReadOnlyList<EquipItem>>((FullEquipType)i, _items[i]); yield return new KeyValuePair<FullEquipType, IReadOnlyList<EquipItem>>((FullEquipType)i, new EquipItemList(_byType[i]));
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator(); => GetEnumerator();
public int Count public int Count
=> _items.Count - 1; => _byType.Count - 1;
public bool ContainsKey(FullEquipType key) public bool ContainsKey(FullEquipType key)
=> (int)key < _items.Count && key != FullEquipType.Unknown; => (int)key < _byType.Count && key != FullEquipType.Unknown;
public bool TryGetValue(FullEquipType key, out IReadOnlyList<EquipItem> value) public bool TryGetValue(FullEquipType key, out IReadOnlyList<EquipItem> value)
{ {
if (ContainsKey(key)) if (ContainsKey(key))
{ {
value = _items[(int)key]; value = new EquipItemList(_byType[(int)key]);
return true; return true;
} }
value = _items[0]; value = Array.Empty<EquipItem>();
return false; return false;
} }
public IReadOnlyList<EquipItem> this[FullEquipType key] public IReadOnlyList<EquipItem> this[FullEquipType key]
=> TryGetValue(key, out var ret) ? ret : throw new IndexOutOfRangeException(); => 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 bool TryGetValue(uint key, bool main, out EquipItem value)
{
var dict = main ? _mainItems : _offItems;
if (dict.TryGetValue(key, out var v))
{
value = v;
return true;
}
value = default;
return false;
}
public IEnumerable<FullEquipType> Keys public IEnumerable<FullEquipType> Keys
=> Enum.GetValues<FullEquipType>().Skip(1); => Enum.GetValues<FullEquipType>().Skip(1);
public IEnumerable<IReadOnlyList<EquipItem>> Values public IEnumerable<IReadOnlyList<EquipItem>> Values
=> _items.Skip(1); => _byType.Skip(1).Select(l => (IReadOnlyList<EquipItem>)new EquipItemList(l));
private readonly struct EquipItemList : IReadOnlyList<EquipItem>
{
private readonly IReadOnlyList<PseudoEquipItem> _items;
public EquipItemList(IReadOnlyList<PseudoEquipItem> items)
=> _items = items;
public IEnumerator<EquipItem> GetEnumerator()
=> _items.Select(i => (EquipItem)i).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
public int Count
=> _items.Count;
public EquipItem this[int index]
=> _items[index];
}
} }

View file

@ -6,10 +6,11 @@ using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
namespace Penumbra.GameData.Data; namespace Penumbra.GameData.Data;
internal sealed class WeaponIdentificationList : KeyList<EquipItem> internal sealed class WeaponIdentificationList : KeyList<PseudoEquipItem>
{ {
private const string Tag = "WeaponIdentification"; private const string Tag = "WeaponIdentification";
private const int Version = 1; private const int Version = 1;
@ -19,16 +20,16 @@ internal sealed class WeaponIdentificationList : KeyList<EquipItem>
{ } { }
public IEnumerable<EquipItem> Between(SetId modelId) public IEnumerable<EquipItem> Between(SetId modelId)
=> Between(ToKey(modelId, 0, 0), ToKey(modelId, 0xFFFF, 0xFF)); => Between(ToKey(modelId, 0, 0), ToKey(modelId, 0xFFFF, 0xFF)).Select(e => (EquipItem)e);
public IEnumerable<EquipItem> Between(SetId modelId, WeaponType type, byte variant = 0) public IEnumerable<EquipItem> Between(SetId modelId, WeaponType type, byte variant = 0)
{ {
if (type == 0) if (type == 0)
return Between(ToKey(modelId, 0, 0), ToKey(modelId, 0xFFFF, 0xFF)); return Between(ToKey(modelId, 0, 0), ToKey(modelId, 0xFFFF, 0xFF)).Select(e => (EquipItem)e);
if (variant == 0) if (variant == 0)
return Between(ToKey(modelId, type, 0), ToKey(modelId, type, 0xFF)); return Between(ToKey(modelId, type, 0), ToKey(modelId, type, 0xFF)).Select(e => (EquipItem)e);
return Between(ToKey(modelId, type, variant), ToKey(modelId, type, variant)); return Between(ToKey(modelId, type, variant), ToKey(modelId, type, variant)).Select(e => (EquipItem)e);
} }
public void Dispose(DalamudPluginInterface pi, ClientLanguage language) public void Dispose(DalamudPluginInterface pi, ClientLanguage language)
@ -40,7 +41,7 @@ internal sealed class WeaponIdentificationList : KeyList<EquipItem>
public static ulong ToKey(EquipItem i) public static ulong ToKey(EquipItem i)
=> ToKey(i.ModelId, i.WeaponType, i.Variant); => ToKey(i.ModelId, i.WeaponType, i.Variant);
protected override IEnumerable<ulong> ToKeys(EquipItem data) protected override IEnumerable<ulong> ToKeys(PseudoEquipItem data)
{ {
yield return ToKey(data); yield return ToKey(data);
} }
@ -48,20 +49,20 @@ internal sealed class WeaponIdentificationList : KeyList<EquipItem>
protected override bool ValidKey(ulong key) protected override bool ValidKey(ulong key)
=> key != 0; => key != 0;
protected override int ValueKeySelector(EquipItem data) protected override int ValueKeySelector(PseudoEquipItem data)
=> (int)data.Id; => (int)data.Item2;
private static IEnumerable<EquipItem> CreateWeaponList(DataManager gameData, ClientLanguage language) private static IEnumerable<PseudoEquipItem> CreateWeaponList(DataManager gameData, ClientLanguage language)
=> gameData.GetExcelSheet<Item>(language)!.SelectMany(ToEquipItems); => gameData.GetExcelSheet<Item>(language)!.SelectMany(ToEquipItems);
private static IEnumerable<EquipItem> ToEquipItems(Item item) private static IEnumerable<PseudoEquipItem> ToEquipItems(Item item)
{ {
if ((EquipSlot)item.EquipSlotCategory.Row is not (EquipSlot.MainHand or EquipSlot.OffHand or EquipSlot.BothHand)) if ((EquipSlot)item.EquipSlotCategory.Row is not (EquipSlot.MainHand or EquipSlot.OffHand or EquipSlot.BothHand))
yield break; yield break;
if (item.ModelMain != 0) if (item.ModelMain != 0)
yield return EquipItem.FromMainhand(item); yield return (PseudoEquipItem)EquipItem.FromMainhand(item);
if (item.ModelSub != 0) if (item.ModelSub != 0)
yield return EquipItem.FromOffhand(item); yield return (PseudoEquipItem)EquipItem.FromOffhand(item);
} }
} }

View file

@ -403,4 +403,7 @@ public static class FullEquipTypeExtensions
public static readonly IReadOnlyList<FullEquipType> AccessoryTypes public static readonly IReadOnlyList<FullEquipType> AccessoryTypes
= Enum.GetValues<FullEquipType>().Where(v => v.IsAccessory()).ToArray(); = Enum.GetValues<FullEquipType>().Where(v => v.IsAccessory()).ToArray();
public static readonly IReadOnlyList<FullEquipType> OffhandTypes
= Enum.GetValues<FullEquipType>().Where(v => v.OffhandTypeSuffix().Length > 0).ToArray();
} }

View file

@ -2,6 +2,7 @@
using Dalamud.Utility; using Dalamud.Utility;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
namespace Penumbra.GameData.Structs; namespace Penumbra.GameData.Structs;
@ -15,7 +16,6 @@ public readonly struct EquipItem
public readonly WeaponType WeaponType; public readonly WeaponType WeaponType;
public readonly byte Variant; public readonly byte Variant;
public readonly FullEquipType Type; public readonly FullEquipType Type;
public readonly EquipSlot Slot;
public bool Valid public bool Valid
=> Type != FullEquipType.Unknown; => Type != FullEquipType.Unknown;
@ -35,8 +35,7 @@ public readonly struct EquipItem
public EquipItem() public EquipItem()
=> Name = string.Empty; => Name = string.Empty;
public EquipItem(string name, uint id, ushort iconId, SetId modelId, WeaponType weaponType, byte variant, FullEquipType type, public EquipItem(string name, uint id, ushort iconId, SetId modelId, WeaponType weaponType, byte variant, FullEquipType type)
EquipSlot slot)
{ {
Name = string.Intern(name); Name = string.Intern(name);
Id = id; Id = id;
@ -45,20 +44,24 @@ public readonly struct EquipItem
WeaponType = weaponType; WeaponType = weaponType;
Variant = variant; Variant = variant;
Type = type; Type = type;
Slot = slot;
} }
public static implicit operator EquipItem(PseudoEquipItem it)
=> new(it.Item1, it.Item2, it.Item3, it.Item4, it.Item5, it.Item6, (FullEquipType)it.Item7);
public static explicit operator PseudoEquipItem(EquipItem it)
=> (it.Name, it.Id, it.IconId, (ushort)it.ModelId, (ushort)it.WeaponType, it.Variant, (byte)it.Type);
public static EquipItem FromArmor(Item item) public static EquipItem FromArmor(Item item)
{ {
var type = item.ToEquipType(); var type = item.ToEquipType();
var slot = type.ToSlot();
var name = item.Name.ToDalamudString().TextValue; var name = item.Name.ToDalamudString().TextValue;
var id = item.RowId; var id = item.RowId;
var icon = item.Icon; var icon = item.Icon;
var model = (SetId)item.ModelMain; var model = (SetId)item.ModelMain;
var weapon = (WeaponType)0; var weapon = (WeaponType)0;
var variant = (byte)(item.ModelMain >> 16); var variant = (byte)(item.ModelMain >> 16);
return new EquipItem(name, id, icon, model, weapon, variant, type, slot); return new EquipItem(name, id, icon, model, weapon, variant, type);
} }
public static EquipItem FromMainhand(Item item) public static EquipItem FromMainhand(Item item)
@ -70,7 +73,7 @@ public readonly struct EquipItem
var model = (SetId)item.ModelMain; var model = (SetId)item.ModelMain;
var weapon = (WeaponType)(item.ModelMain >> 16); var weapon = (WeaponType)(item.ModelMain >> 16);
var variant = (byte)(item.ModelMain >> 32); var variant = (byte)(item.ModelMain >> 32);
return new EquipItem(name, id, icon, model, weapon, variant, type, EquipSlot.MainHand); return new EquipItem(name, id, icon, model, weapon, variant, type);
} }
public static EquipItem FromOffhand(Item item) public static EquipItem FromOffhand(Item item)
@ -82,6 +85,6 @@ public readonly struct EquipItem
var model = (SetId)item.ModelSub; var model = (SetId)item.ModelSub;
var weapon = (WeaponType)(item.ModelSub >> 16); var weapon = (WeaponType)(item.ModelSub >> 16);
var variant = (byte)(item.ModelSub >> 32); var variant = (byte)(item.ModelSub >> 32);
return new EquipItem(name, id, icon, model, weapon, variant, type, EquipSlot.OffHand); return new EquipItem(name, id, icon, model, weapon, variant, type);
} }
} }

View file

@ -242,10 +242,10 @@ public static class EquipmentSwap
private static void LookupItem(EquipItem i, out EquipSlot slot, out SetId modelId, out byte variant) private static void LookupItem(EquipItem i, out EquipSlot slot, out SetId modelId, out byte variant)
{ {
if (!i.Slot.IsEquipmentPiece()) slot = i.Type.ToSlot();
if (!slot.IsEquipmentPiece())
throw new ItemSwap.InvalidItemTypeException(); throw new ItemSwap.InvalidItemTypeException();
slot = i.Slot;
modelId = i.ModelId; modelId = i.ModelId;
variant = i.Variant; variant = i.Variant;
} }

View file

@ -5,7 +5,7 @@ using Penumbra.Util;
namespace Penumbra.Services; namespace Penumbra.Services;
public abstract class SyncServiceWrapper<T> public abstract class SyncServiceWrapper<T> : IDisposable
{ {
public string Name { get; } public string Name { get; }
public T Service { get; } public T Service { get; }
@ -34,7 +34,7 @@ public abstract class SyncServiceWrapper<T>
} }
} }
public abstract class AsyncServiceWrapper<T> public abstract class AsyncServiceWrapper<T> : IDisposable
{ {
public string Name { get; } public string Name { get; }
public T? Service { get; private set; } public T? Service { get; private set; }

View file

@ -219,7 +219,7 @@ public class ChangedItemDrawer : IDisposable
switch (obj) switch (obj)
{ {
case EquipItem it: case EquipItem it:
iconType = it.Slot switch iconType = it.Type.ToSlot() switch
{ {
EquipSlot.MainHand => ChangedItemIcon.Mainhand, EquipSlot.MainHand => ChangedItemIcon.Mainhand,
EquipSlot.OffHand => ChangedItemIcon.Offhand, EquipSlot.OffHand => ChangedItemIcon.Offhand,