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 Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
namespace Penumbra.GameData.Data;
internal sealed class EquipmentIdentificationList : KeyList<EquipItem>
internal sealed class EquipmentIdentificationList : KeyList<PseudoEquipItem>
{
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)
{
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)
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)
@ -34,9 +35,9 @@ internal sealed class EquipmentIdentificationList : KeyList<EquipItem>
=> ((ulong)modelId << 32) | ((ulong)slot << 16) | variant;
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);
}
@ -44,12 +45,12 @@ internal sealed class EquipmentIdentificationList : KeyList<EquipItem>
protected override bool ValidKey(ulong key)
=> key != 0;
protected override int ValueKeySelector(EquipItem data)
=> (int)data.Id;
protected override int ValueKeySelector(PseudoEquipItem data)
=> (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)!;
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 Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -18,7 +17,6 @@ public static partial class GamePaths
: GenderRace.Unknown;
}
public static partial class Monster
{
public static partial class Imc

View file

@ -8,14 +8,17 @@ using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
namespace Penumbra.GameData.Data;
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();
@ -28,7 +31,7 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IR
if (item.ModelMain != 0)
tmp[(int)type].Add(EquipItem.FromMainhand(item));
if (item.ModelSub != 0)
tmp[(int)type].Add(EquipItem.FromOffhand(item));
tmp[(int)type.Offhand()].Add(EquipItem.FromOffhand(item));
}
else if (type != FullEquipType.Unknown)
{
@ -36,56 +39,141 @@ public sealed class ItemData : DataSharer, IReadOnlyDictionary<FullEquipType, IR
}
}
var ret = new IReadOnlyList<EquipItem>[tmp.Length];
ret[0] = Array.Empty<EquipItem>();
var ret = new IReadOnlyList<PseudoEquipItem>[tmp.Length];
ret[0] = Array.Empty<PseudoEquipItem>();
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;
}
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)
: 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()
=> DisposeTag("ItemList");
{
DisposeTag("ItemList");
DisposeTag("ItemDictMain");
DisposeTag("ItemDictOff");
}
public IEnumerator<KeyValuePair<FullEquipType, IReadOnlyList<EquipItem>>> GetEnumerator()
{
for (var i = 1; i < _items.Count; ++i)
yield return new KeyValuePair<FullEquipType, IReadOnlyList<EquipItem>>((FullEquipType)i, _items[i]);
for (var i = 1; i < _byType.Count; ++i)
yield return new KeyValuePair<FullEquipType, IReadOnlyList<EquipItem>>((FullEquipType)i, new EquipItemList(_byType[i]));
}
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
public int Count
=> _items.Count - 1;
=> _byType.Count - 1;
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)
{
if (ContainsKey(key))
{
value = _items[(int)key];
value = new EquipItemList(_byType[(int)key]);
return true;
}
value = _items[0];
value = Array.Empty<EquipItem>();
return false;
}
public IReadOnlyList<EquipItem> 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 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
=> Enum.GetValues<FullEquipType>().Skip(1);
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 Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
namespace Penumbra.GameData.Data;
internal sealed class WeaponIdentificationList : KeyList<EquipItem>
internal sealed class WeaponIdentificationList : KeyList<PseudoEquipItem>
{
private const string Tag = "WeaponIdentification";
private const int Version = 1;
@ -19,16 +20,16 @@ internal sealed class WeaponIdentificationList : KeyList<EquipItem>
{ }
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)
{
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)
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)
@ -40,7 +41,7 @@ internal sealed class WeaponIdentificationList : KeyList<EquipItem>
public static ulong ToKey(EquipItem i)
=> 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);
}
@ -48,20 +49,20 @@ internal sealed class WeaponIdentificationList : KeyList<EquipItem>
protected override bool ValidKey(ulong key)
=> key != 0;
protected override int ValueKeySelector(EquipItem data)
=> (int)data.Id;
protected override int ValueKeySelector(PseudoEquipItem data)
=> (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);
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))
yield break;
if (item.ModelMain != 0)
yield return EquipItem.FromMainhand(item);
yield return (PseudoEquipItem)EquipItem.FromMainhand(item);
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
= 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 Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
using PseudoEquipItem = System.ValueTuple<string, uint, ushort, ushort, ushort, byte, byte>;
namespace Penumbra.GameData.Structs;
@ -15,7 +16,6 @@ public readonly struct EquipItem
public readonly WeaponType WeaponType;
public readonly byte Variant;
public readonly FullEquipType Type;
public readonly EquipSlot Slot;
public bool Valid
=> Type != FullEquipType.Unknown;
@ -35,8 +35,7 @@ public readonly struct EquipItem
public EquipItem()
=> Name = string.Empty;
public EquipItem(string name, uint id, ushort iconId, SetId modelId, WeaponType weaponType, byte variant, FullEquipType type,
EquipSlot slot)
public EquipItem(string name, uint id, ushort iconId, SetId modelId, WeaponType weaponType, byte variant, FullEquipType type)
{
Name = string.Intern(name);
Id = id;
@ -45,20 +44,24 @@ public readonly struct EquipItem
WeaponType = weaponType;
Variant = variant;
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)
{
var type = item.ToEquipType();
var slot = type.ToSlot();
var name = item.Name.ToDalamudString().TextValue;
var id = item.RowId;
var icon = item.Icon;
var model = (SetId)item.ModelMain;
var weapon = (WeaponType)0;
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)
@ -70,7 +73,7 @@ public readonly struct EquipItem
var model = (SetId)item.ModelMain;
var weapon = (WeaponType)(item.ModelMain >> 16);
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)
@ -82,6 +85,6 @@ public readonly struct EquipItem
var model = (SetId)item.ModelSub;
var weapon = (WeaponType)(item.ModelSub >> 16);
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)
{
if (!i.Slot.IsEquipmentPiece())
slot = i.Type.ToSlot();
if (!slot.IsEquipmentPiece())
throw new ItemSwap.InvalidItemTypeException();
slot = i.Slot;
modelId = i.ModelId;
variant = i.Variant;
}

View file

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

View file

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