diff --git a/Glamourer/Automation/AutoDesignManager.cs b/Glamourer/Automation/AutoDesignManager.cs index a69a264..619ba64 100644 --- a/Glamourer/Automation/AutoDesignManager.cs +++ b/Glamourer/Automation/AutoDesignManager.cs @@ -513,10 +513,11 @@ public class AutoDesignManager : ISavable, IReadOnlyList { ObjectKind.BattleNpc => manager.Data.BNpcs, ObjectKind.EventNpc => manager.Data.ENpcs, - _ => throw new NotImplementedException(), + _ => new Dictionary(), }; return table.Where(kvp => kvp.Value == name) - .Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld, identifier.Kind, + .Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld.Id, + identifier.Kind, kvp.Key)).ToArray(); } diff --git a/Glamourer/Designs/DesignBase.cs b/Glamourer/Designs/DesignBase.cs index 73546ab..aab48bc 100644 --- a/Glamourer/Designs/DesignBase.cs +++ b/Glamourer/Designs/DesignBase.cs @@ -168,11 +168,11 @@ public class DesignBase protected JObject SerializeEquipment() { - static JObject Serialize(ulong id, StainId stain, bool apply, bool applyStain) + static JObject Serialize(CustomItemId id, StainId stain, bool apply, bool applyStain) => new() { - ["ItemId"] = id, - ["Stain"] = stain.Value, + ["ItemId"] = id.Id, + ["Stain"] = stain.Id, ["Apply"] = apply, ["ApplyStain"] = applyStain, }; @@ -267,9 +267,9 @@ public class DesignBase return; } - static (ulong, StainId, bool, bool) ParseItem(EquipSlot slot, JToken? item) + static (CustomItemId, StainId, bool, bool) ParseItem(EquipSlot slot, JToken? item) { - var id = item?["ItemId"]?.ToObject() ?? ItemManager.NothingId(slot); + var id = item?["ItemId"]?.ToObject() ?? ItemManager.NothingId(slot).Id; var stain = (StainId)(item?["Stain"]?.ToObject() ?? 0); var apply = item?["Apply"]?.ToObject() ?? false; var applyStain = item?["ApplyStain"]?.ToObject() ?? false; @@ -302,7 +302,7 @@ public class DesignBase if (id == ItemManager.NothingId(EquipSlot.OffHand)) id = ItemManager.NothingId(FullEquipType.Shield); - PrintWarning(items.ValidateWeapons((uint)id, (uint)idOff, out var main, out var off)); + PrintWarning(items.ValidateWeapons(id.Item, idOff.Item, out var main, out var off)); PrintWarning(items.ValidateStain(stain, out stain, allowUnknown)); PrintWarning(items.ValidateStain(stainOff, out stainOff, allowUnknown)); design.DesignData.SetItem(EquipSlot.MainHand, main); diff --git a/Glamourer/Designs/DesignBase64Migration.cs b/Glamourer/Designs/DesignBase64Migration.cs index 852c7a6..b4557a3 100644 --- a/Glamourer/Designs/DesignBase64Migration.cs +++ b/Glamourer/Designs/DesignBase64Migration.cs @@ -113,42 +113,42 @@ public class DesignBase64Migration var mdl = eq[idx]; var item = items.Identify(slot, mdl.Set, mdl.Variant); if (!item.Valid) - throw new Exception($"Base64 string invalid, item could not be identified."); + throw new Exception("Base64 string invalid, item could not be identified."); data.SetItem(slot, item); data.SetStain(slot, mdl.Stain); } - var main = cur[0].Set.Value == 0 + var main = cur[0].Set.Id == 0 ? items.DefaultSword - : items.Identify(EquipSlot.MainHand, cur[0].Set, cur[0].Type, (byte)cur[0].Variant); + : items.Identify(EquipSlot.MainHand, cur[0].Set, cur[0].Type, cur[0].Variant); if (!main.Valid) - throw new Exception($"Base64 string invalid, weapon could not be identified."); + throw new Exception("Base64 string invalid, weapon could not be identified."); data.SetItem(EquipSlot.MainHand, main); data.SetStain(EquipSlot.MainHand, cur[0].Stain); EquipItem off; // Fist weapon hack - if (main.ModelId.Value is > 1600 and < 1651 && cur[1].Variant == 0) + if (main.ModelId.Id is > 1600 and < 1651 && cur[1].Variant == 0) { - off = items.Identify(EquipSlot.OffHand, (SetId)(main.ModelId.Value + 50), main.WeaponType, main.Variant, main.Type); - var gauntlet = items.Identify(EquipSlot.Hands, cur[1].Set, (byte)cur[1].Type); + off = items.Identify(EquipSlot.OffHand, (SetId)(main.ModelId.Id + 50), main.WeaponType, main.Variant, main.Type); + var gauntlet = items.Identify(EquipSlot.Hands, cur[1].Set, (Variant)cur[1].Type.Id); if (!gauntlet.Valid) - throw new Exception($"Base64 string invalid, item could not be identified."); + throw new Exception("Base64 string invalid, item could not be identified."); data.SetItem(EquipSlot.Hands, gauntlet); data.SetStain(EquipSlot.Hands, cur[0].Stain); } else { - off = cur[0].Set.Value == 0 + off = cur[0].Set.Id == 0 ? ItemManager.NothingItem(FullEquipType.Shield) - : items.Identify(EquipSlot.OffHand, cur[1].Set, cur[1].Type, (byte)cur[1].Variant, main.Type); + : items.Identify(EquipSlot.OffHand, cur[1].Set, cur[1].Type, cur[1].Variant, main.Type); } if (main.Type.ValidOffhand() != FullEquipType.Unknown && !off.Valid) - throw new Exception($"Base64 string invalid, weapon could not be identified."); + throw new Exception("Base64 string invalid, weapon could not be identified."); data.SetItem(EquipSlot.OffHand, off); data.SetStain(EquipSlot.OffHand, cur[1].Stain); diff --git a/Glamourer/Designs/DesignData.cs b/Glamourer/Designs/DesignData.cs index 79f1e0e..b7bee4a 100644 --- a/Glamourer/Designs/DesignData.cs +++ b/Glamourer/Designs/DesignData.cs @@ -95,11 +95,11 @@ public unsafe struct DesignData if (index > 11) return false; - _itemIds[index] = item.ItemId; - _iconIds[index] = item.IconId; - _equipmentBytes[4 * index + 0] = (byte)item.ModelId; - _equipmentBytes[4 * index + 1] = (byte)(item.ModelId.Value >> 8); - _equipmentBytes[4 * index + 2] = item.Variant; + _itemIds[index] = item.ItemId.Id; + _iconIds[index] = item.IconId.Id; + _equipmentBytes[4 * index + 0] = (byte)item.ModelId.Id; + _equipmentBytes[4 * index + 1] = (byte)(item.ModelId.Id >> 8); + _equipmentBytes[4 * index + 2] = item.Variant.Id; switch (index) { // @formatter:off @@ -132,18 +132,18 @@ public unsafe struct DesignData public bool SetStain(EquipSlot slot, StainId stain) => slot.ToIndex() switch { - 0 => SetIfDifferent(ref _equipmentBytes[3], stain.Value), - 1 => SetIfDifferent(ref _equipmentBytes[7], stain.Value), - 2 => SetIfDifferent(ref _equipmentBytes[11], stain.Value), - 3 => SetIfDifferent(ref _equipmentBytes[15], stain.Value), - 4 => SetIfDifferent(ref _equipmentBytes[19], stain.Value), - 5 => SetIfDifferent(ref _equipmentBytes[23], stain.Value), - 6 => SetIfDifferent(ref _equipmentBytes[27], stain.Value), - 7 => SetIfDifferent(ref _equipmentBytes[31], stain.Value), - 8 => SetIfDifferent(ref _equipmentBytes[35], stain.Value), - 9 => SetIfDifferent(ref _equipmentBytes[39], stain.Value), - 10 => SetIfDifferent(ref _equipmentBytes[43], stain.Value), - 11 => SetIfDifferent(ref _equipmentBytes[47], stain.Value), + 0 => SetIfDifferent(ref _equipmentBytes[3], stain.Id), + 1 => SetIfDifferent(ref _equipmentBytes[7], stain.Id), + 2 => SetIfDifferent(ref _equipmentBytes[11], stain.Id), + 3 => SetIfDifferent(ref _equipmentBytes[15], stain.Id), + 4 => SetIfDifferent(ref _equipmentBytes[19], stain.Id), + 5 => SetIfDifferent(ref _equipmentBytes[23], stain.Id), + 6 => SetIfDifferent(ref _equipmentBytes[27], stain.Id), + 7 => SetIfDifferent(ref _equipmentBytes[31], stain.Id), + 8 => SetIfDifferent(ref _equipmentBytes[35], stain.Id), + 9 => SetIfDifferent(ref _equipmentBytes[39], stain.Id), + 10 => SetIfDifferent(ref _equipmentBytes[43], stain.Id), + 11 => SetIfDifferent(ref _equipmentBytes[47], stain.Id), _ => false, }; diff --git a/Glamourer/Designs/DesignManager.cs b/Glamourer/Designs/DesignManager.cs index 61ba08a..13d9706 100644 --- a/Glamourer/Designs/DesignManager.cs +++ b/Glamourer/Designs/DesignManager.cs @@ -398,7 +398,7 @@ public class DesignManager design.LastEdit = DateTimeOffset.UtcNow; _saveService.QueueSave(design); - Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stain.Value}."); + Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stain.Id}."); _event.Invoke(DesignChanged.Type.Stain, design, (oldStain, stain, slot)); } diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.cs index 0d070a3..80b1a84 100644 --- a/Glamourer/Gui/Equipment/EquipmentDrawer.cs +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.cs @@ -43,7 +43,7 @@ public class EquipmentDrawer _stainData = items.Stains; _stainCombo = new FilterComboColors(DefaultWidth - 20, _stainData.Data.Prepend(new KeyValuePair(0, ("None", 0, false)))); - _itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, textures)).ToArray(); + _itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e)).ToArray(); _weaponCombo = new Dictionary(FullEquipTypeExtensions.WeaponTypes.Count * 2); foreach (var type in Enum.GetValues()) { @@ -118,7 +118,7 @@ public class EquipmentDrawer bool allWeapons, out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked) { - if (cMainhand.ModelId.Value == 0) + if (cMainhand.ModelId.Id == 0) { rOffhand = cOffhand; rMainhand = cMainhand; @@ -239,7 +239,7 @@ public class EquipmentDrawer if (change) armor = combo.CurrentSelection; - if (!locked && armor.ModelId.Value != 0) + if (!locked && armor.ModelId.Id != 0) { ImGuiUtil.HoverTooltip("Right-click to clear."); if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) @@ -282,15 +282,15 @@ public class EquipmentDrawer /// Draw an input for armor that can set arbitrary values instead of choosing items. private bool DrawArmorArtisan(EquipSlot slot, EquipItem current, out EquipItem armor) { - int setId = current.ModelId.Value; - int variant = current.Variant; + int setId = current.ModelId.Id; + int variant = current.Variant.Id; var ret = false; armor = current; ImGui.SetNextItemWidth(80 * ImGuiHelpers.GlobalScale); if (ImGui.InputInt("##setId", ref setId, 0, 0)) { var newSetId = (SetId)Math.Clamp(setId, 0, ushort.MaxValue); - if (newSetId.Value != current.ModelId.Value) + if (newSetId.Id != current.ModelId.Id) { armor = _items.Identify(slot, newSetId, current.Variant); ret = true; @@ -315,7 +315,7 @@ public class EquipmentDrawer /// Draw an input for stain that can set arbitrary values instead of choosing valid stains. private bool DrawStainArtisan(EquipSlot slot, StainId current, out StainId stain) { - int stainId = current.Value; + int stainId = current.Id; ImGui.SetNextItemWidth(40 * ImGuiHelpers.GlobalScale); if (ImGui.InputInt("##stain", ref stainId, 0, 0)) { diff --git a/Glamourer/Gui/Equipment/ItemCombo.cs b/Glamourer/Gui/Equipment/ItemCombo.cs index 3ced7c4..d3d51ea 100644 --- a/Glamourer/Gui/Equipment/ItemCombo.cs +++ b/Glamourer/Gui/Equipment/ItemCombo.cs @@ -15,16 +15,13 @@ namespace Glamourer.Gui.Equipment; public sealed class ItemCombo : FilterComboCache { - private readonly TextureService _textures; - public readonly string Label; - private uint _currentItem; + private ItemId _currentItem; private float _innerWidth; - public ItemCombo(DataManager gameData, ItemManager items, EquipSlot slot, TextureService textures) + public ItemCombo(DataManager gameData, ItemManager items, EquipSlot slot) : base(() => GetItems(items, slot)) { - _textures = textures; Label = GetLabel(gameData, slot); _currentItem = ItemManager.NothingId(slot); SearchByParts = true; @@ -47,7 +44,7 @@ public sealed class ItemCombo : FilterComboCache return base.UpdateCurrentSelected(CurrentSelectionIdx); } - public bool Draw(string previewName, uint previewIdx, float width, float innerWidth) + public bool Draw(string previewName, ItemId previewIdx, float width, float innerWidth) { _innerWidth = innerWidth; _currentItem = previewIdx; @@ -64,12 +61,12 @@ public sealed class ItemCombo : FilterComboCache var ret = ImGui.Selectable(name, selected); ImGui.SameLine(); using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080); - ImGuiUtil.RightAlign($"({obj.ModelId.Value}-{obj.Variant})"); + ImGuiUtil.RightAlign($"({obj.ModelString})"); return ret; } protected override bool IsVisible(int globalIndex, LowerString filter) - => base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelId.Value.ToString()); + => base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelId.Id.ToString()); protected override string ToString(EquipItem obj) => obj.Name; diff --git a/Glamourer/Gui/Equipment/WeaponCombo.cs b/Glamourer/Gui/Equipment/WeaponCombo.cs index 75d2adb..9540dc1 100644 --- a/Glamourer/Gui/Equipment/WeaponCombo.cs +++ b/Glamourer/Gui/Equipment/WeaponCombo.cs @@ -15,7 +15,7 @@ namespace Glamourer.Gui.Equipment; public sealed class WeaponCombo : FilterComboCache { public readonly string Label; - private uint _currentItemId; + private ItemId _currentItemId; private float _innerWidth; public WeaponCombo(ItemManager items, FullEquipType type) @@ -45,7 +45,7 @@ public sealed class WeaponCombo : FilterComboCache protected override float GetFilterWidth() => _innerWidth - 2 * ImGui.GetStyle().FramePadding.X; - public bool Draw(string previewName, uint previewId, float width, float innerWidth) + public bool Draw(string previewName, ItemId previewId, float width, float innerWidth) { _currentItemId = previewId; _innerWidth = innerWidth; @@ -59,12 +59,12 @@ public sealed class WeaponCombo : FilterComboCache var ret = ImGui.Selectable(name, selected); ImGui.SameLine(); using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080); - ImGuiUtil.RightAlign($"({obj.ModelId.Value}-{obj.WeaponType.Value}-{obj.Variant})"); + ImGuiUtil.RightAlign($"({obj.ModelId.Id}-{obj.WeaponType.Id}-{obj.Variant})"); return ret; } protected override bool IsVisible(int globalIndex, LowerString filter) - => base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelId.Value.ToString()); + => base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelId.Id.ToString()); protected override string ToString(EquipItem obj) => obj.Name; diff --git a/Glamourer/Gui/PenumbraChangedItemTooltip.cs b/Glamourer/Gui/PenumbraChangedItemTooltip.cs index 62e9ba2..a0ea92d 100644 --- a/Glamourer/Gui/PenumbraChangedItemTooltip.cs +++ b/Glamourer/Gui/PenumbraChangedItemTooltip.cs @@ -173,7 +173,7 @@ public class PenumbraChangedItemTooltip : IDisposable private bool CanApplyWeapon(EquipSlot slot, EquipItem item) { var main = _objects.Player.GetMainhand(); - var mainItem = _items.Identify(slot, main.Set, main.Type, (byte)main.Variant); + var mainItem = _items.Identify(slot, main.Set, main.Type, main.Variant); if (slot == EquipSlot.MainHand) return item.Type == mainItem.Type; diff --git a/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs b/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs index ae1d1d4..12845e4 100644 --- a/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs +++ b/Glamourer/Gui/Tabs/AutomationTab/HumanNpcCombo.cs @@ -78,7 +78,7 @@ public sealed class HumanNpcCombo : FilterComboCache<(string Name, ObjectKind Ki { case ObjectKind.BattleNpc: var nameIds = service.AwaitedService.GetBnpcNames(id); - ret.AddRange(nameIds.Select(nameId => (name, kind, nameId))); + ret.AddRange(nameIds.Select(nameId => (service.AwaitedService.Name(ObjectKind.BattleNpc, nameId), kind, nameId.Id))); break; case ObjectKind.EventNpc: ret.Add((name, kind, id)); @@ -87,8 +87,10 @@ public sealed class HumanNpcCombo : FilterComboCache<(string Name, ObjectKind Ki } } - return ret.GroupBy(t => (t.Name, t.Kind)).OrderBy(g => g.Key, Comparer) - .Select(g => (g.Key.Name, g.Key.Kind, g.Select(g => g.Id).ToArray())).ToList(); + return ret.GroupBy(t => (t.Name, t.Kind)) + .OrderBy(g => g.Key, Comparer) + .Select(g => (g.Key.Name, g.Key.Kind, g.Select(g => g.Id).Distinct().ToArray())) + .ToList(); } private static readonly NameComparer Comparer = new(); diff --git a/Glamourer/Gui/Tabs/DebugTab.cs b/Glamourer/Gui/Tabs/DebugTab.cs index fba97a2..d3c8d3a 100644 --- a/Glamourer/Gui/Tabs/DebugTab.cs +++ b/Glamourer/Gui/Tabs/DebugTab.cs @@ -640,16 +640,17 @@ public unsafe class DebugTab : ITab foreach (var slot in EquipSlotExtensions.EqdpSlots) { - var identified = _items.Identify(slot, (SetId)_setId, (byte)_variant); + var identified = _items.Identify(slot, (SetId)_setId, (Variant)_variant); Text(identified.Name); ImGuiUtil.HoverTooltip(string.Join("\n", - _items.IdentifierService.AwaitedService.Identify((SetId)_setId, (ushort)_variant, slot).Select(i => $"{i.Name} {i.Id} {i.ItemId} {i.IconId}"))); + _items.IdentifierService.AwaitedService.Identify((SetId)_setId, (Variant)_variant, slot) + .Select(i => $"{i.Name} {i.Id} {i.ItemId} {i.IconId}"))); } - var weapon = _items.Identify(EquipSlot.MainHand, (SetId)_setId, (WeaponType)_secondaryId, (byte)_variant); + var weapon = _items.Identify(EquipSlot.MainHand, (SetId)_setId, (WeaponType)_secondaryId, (Variant)_variant); Text(weapon.Name); ImGuiUtil.HoverTooltip(string.Join("\n", - _items.IdentifierService.AwaitedService.Identify((SetId)_setId, (WeaponType)_secondaryId, (ushort)_variant, EquipSlot.MainHand))); + _items.IdentifierService.AwaitedService.Identify((SetId)_setId, (WeaponType)_secondaryId, (Variant)_variant, EquipSlot.MainHand))); } private void DrawRestrictedGear() @@ -672,7 +673,7 @@ public unsafe class DebugTab : ITab foreach (var slot in EquipSlotExtensions.EqdpSlots) { var (replaced, model) = - _items.RestrictedGear.ResolveRestricted(new CharacterArmor((SetId)_setId, (byte)_variant, 0), slot, race, gender); + _items.RestrictedGear.ResolveRestricted(new CharacterArmor((SetId)_setId, (Variant)_variant, 0), slot, race, gender); if (replaced) ImGui.TextUnformatted($"{race.ToName()} - {gender} - {slot.ToName()} resolves to {model}."); } @@ -765,18 +766,18 @@ public unsafe class DebugTab : ITab ImRaii.TreeNode($"Default Sword: {_items.DefaultSword.Name} ({_items.DefaultSword.ItemId}) ({_items.DefaultSword.Weapon()})", ImGuiTreeNodeFlags.Leaf).Dispose(); DrawNameTable("All Items (Main)", ref _itemFilter, - _items.ItemService.AwaitedService.AllItems(true).Select(p => (p.Item1, + _items.ItemService.AwaitedService.AllItems(true).Select(p => (p.Item1.Id, $"{p.Item2.Name} ({(p.Item2.WeaponType == 0 ? p.Item2.Armor().ToString() : p.Item2.Weapon().ToString())})")) .OrderBy(p => p.Item1)); DrawNameTable("All Items (Off)", ref _itemFilter, - _items.ItemService.AwaitedService.AllItems(false).Select(p => (p.Item1, + _items.ItemService.AwaitedService.AllItems(false).Select(p => (p.Item1.Id, $"{p.Item2.Name} ({(p.Item2.WeaponType == 0 ? p.Item2.Armor().ToString() : p.Item2.Weapon().ToString())})")) .OrderBy(p => p.Item1)); foreach (var type in Enum.GetValues().Skip(1)) { DrawNameTable(type.ToName(), ref _itemFilter, _items.ItemService.AwaitedService[type] - .Select(p => (Id: p.ItemId, $"{p.Name} ({(p.WeaponType == 0 ? p.Armor().ToString() : p.Weapon().ToString())})"))); + .Select(p => (Id: p.ItemId.Id, $"{p.Name} ({(p.WeaponType == 0 ? p.Armor().ToString() : p.Weapon().ToString())})"))); } } @@ -803,10 +804,10 @@ public unsafe class DebugTab : ITab var skips = ImGuiClip.GetNecessarySkips(height); ImGui.TableNextRow(); var remainder = ImGuiClip.FilteredClippedDraw(_items.Stains, skips, - p => p.Key.Value.ToString().Contains(_stainFilter) || p.Value.Name.Contains(_stainFilter, StringComparison.OrdinalIgnoreCase), + p => p.Key.Id.ToString().Contains(_stainFilter) || p.Value.Name.Contains(_stainFilter, StringComparison.OrdinalIgnoreCase), p => { - ImGuiUtil.DrawTableColumn(p.Key.Value.ToString("D3")); + ImGuiUtil.DrawTableColumn(p.Key.Id.ToString("D3")); ImGui.TableNextColumn(); ImGui.GetWindowDrawList().AddRectFilled(ImGui.GetCursorScreenPos(), ImGui.GetCursorScreenPos() + new Vector2(ImGui.GetTextLineHeight()), @@ -1038,9 +1039,9 @@ public unsafe class DebugTab : ITab try { - _clipboardText = ImGui.GetClipboardText(); - _clipboardData = Convert.FromBase64String(_clipboardText); - _version = _clipboardData[0]; + _clipboardText = ImGui.GetClipboardText(); + _clipboardData = Convert.FromBase64String(_clipboardText); + _version = _clipboardData[0]; if (_version == 5) _clipboardData = _clipboardData[DesignBase64Migration.Base64SizeV4..]; _version = _clipboardData.Decompress(out _dataUncompressed); @@ -1116,7 +1117,7 @@ public unsafe class DebugTab : ITab static string ItemString(in DesignData data, EquipSlot slot) { var item = data.Item(slot); - return $"{item.Name} ({item.ModelId.Value}{(item.WeaponType != 0 ? $"-{item.WeaponType.Value}" : string.Empty)}-{item.Variant})"; + return $"{item.Name} ({item.ModelId.Id}{(item.WeaponType != 0 ? $"-{item.WeaponType.Id}" : string.Empty)}-{item.Variant})"; } PrintRow("Model ID", state.BaseData.ModelId, state.ModelData.ModelId, state[ActorState.MetaIndex.ModelId]); @@ -1137,8 +1138,8 @@ public unsafe class DebugTab : ITab foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand)) { PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state[slot, false]); - ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Value.ToString()); - ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Value.ToString()); + ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Id.ToString()); + ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Id.ToString()); ImGuiUtil.DrawTableColumn(state[slot, true].ToString()); } @@ -1453,7 +1454,7 @@ public unsafe class DebugTab : ITab ImGui.TableNextColumn(); var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing()); ImGui.TableNextRow(); - var remainder = ImGuiClip.ClippedDraw(_itemUnlocks.Unlocked, skips, t => + var remainder = ImGuiClip.ClippedDraw(_itemUnlocks, skips, t => { ImGuiUtil.DrawTableColumn(t.Key.ToString()); if (_items.ItemService.AwaitedService.TryGetValue(t.Key, EquipSlot.MainHand, out var equip)) @@ -1474,7 +1475,7 @@ public unsafe class DebugTab : ITab ? "Always" : time.LocalDateTime.ToString("g") : "Never"); - }, _itemUnlocks.Unlocked.Count); + }, _itemUnlocks.Count); ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeight()); } diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs index 06ebe9b..c55248b 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs @@ -154,7 +154,7 @@ public class UnlockOverview void DrawItem(EquipItem item) { var unlocked = _itemUnlocks.IsUnlocked(item.Id, out var time); - var iconHandle = _textures.LoadIcon(item.IconId); + var iconHandle = _textures.LoadIcon(item.IconId.Id); if (!iconHandle.HasValue) return; diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs index 8473ce7..5bd5f73 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs @@ -61,7 +61,7 @@ public class UnlockTable : Table, IDisposable public override void DrawColumn(EquipItem item, int _) { - var iconHandle = _textures.LoadIcon(item.IconId); + var iconHandle = _textures.LoadIcon(item.IconId.Id); if (iconHandle.HasValue) ImGuiUtil.HoverIcon(iconHandle.Value, new Vector2(ImGui.GetFrameHeight())); else @@ -214,7 +214,7 @@ public class UnlockTable : Table, IDisposable => 70 * ImGuiHelpers.GlobalScale; public override int Compare(EquipItem lhs, EquipItem rhs) - => lhs.ItemId.CompareTo(rhs.ItemId); + => lhs.ItemId.Id.CompareTo(rhs.ItemId.Id); public override string ToName(EquipItem item) => item.ItemId.ToString(); diff --git a/Glamourer/Gui/UiHelpers.cs b/Glamourer/Gui/UiHelpers.cs index 1050ea5..b9e9c6c 100644 --- a/Glamourer/Gui/UiHelpers.cs +++ b/Glamourer/Gui/UiHelpers.cs @@ -27,7 +27,7 @@ public static class UiHelpers { public static void DrawIcon(this EquipItem item, TextureService textures, Vector2 size) { - var isEmpty = item.ModelId.Value == 0; + var isEmpty = item.ModelId.Id == 0; var (ptr, textureSize, empty) = textures.GetIcon(item); if (empty) { diff --git a/Glamourer/Interop/ContextMenuService.cs b/Glamourer/Interop/ContextMenuService.cs index 55bd0ba..1ed4a99 100644 --- a/Glamourer/Interop/ContextMenuService.cs +++ b/Glamourer/Interop/ContextMenuService.cs @@ -119,7 +119,7 @@ public class ContextMenuService : IDisposable _state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual); if (item.Type.ValidOffhand().IsOffhandType()) { - if (item.ModelId.Value is > 1600 and < 1651 + if (item.ModelId.Id is > 1600 and < 1651 && _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets)) _state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual); if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand)) @@ -143,7 +143,7 @@ public class ContextMenuService : IDisposable _state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual); if (item.Type.ValidOffhand().IsOffhandType()) { - if (item.ModelId.Value is > 1600 and < 1651 + if (item.ModelId.Id is > 1600 and < 1651 && _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets)) _state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual); if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand)) diff --git a/Glamourer/Interop/Structs/Model.cs b/Glamourer/Interop/Structs/Model.cs index 76d15a5..4da70ec 100644 --- a/Glamourer/Interop/Structs/Model.cs +++ b/Glamourer/Interop/Structs/Model.cs @@ -98,7 +98,7 @@ public readonly unsafe struct Model : IEquatable Model weapon = AsDrawObject->Object.ChildObject; return !weapon.IsWeapon ? (Null, CharacterWeapon.Empty) - : (weapon, new CharacterWeapon(weapon.AsWeapon->ModelSetId, weapon.AsWeapon->SecondaryId, weapon.AsWeapon->Variant, + : (weapon, new CharacterWeapon(weapon.AsWeapon->ModelSetId, weapon.AsWeapon->SecondaryId, (Variant)weapon.AsWeapon->Variant, (StainId)weapon.AsWeapon->ModelUnknown)); } @@ -112,7 +112,7 @@ public readonly unsafe struct Model : IEquatable if (offhand == mainhand || !offhand.IsWeapon) return (Null, CharacterWeapon.Empty); - return (offhand, new CharacterWeapon(offhand.AsWeapon->ModelSetId, offhand.AsWeapon->SecondaryId, offhand.AsWeapon->Variant, + return (offhand, new CharacterWeapon(offhand.AsWeapon->ModelSetId, offhand.AsWeapon->SecondaryId, (Variant)offhand.AsWeapon->Variant, (StainId)offhand.AsWeapon->ModelUnknown)); } @@ -124,13 +124,14 @@ public readonly unsafe struct Model : IEquatable { case 0: return (Null, Null, CharacterWeapon.Empty, CharacterWeapon.Empty); case 1: - return (first, Null, new CharacterWeapon(first.AsWeapon->ModelSetId, first.AsWeapon->SecondaryId, first.AsWeapon->Variant, + return (first, Null, new CharacterWeapon(first.AsWeapon->ModelSetId, first.AsWeapon->SecondaryId, + (Variant)first.AsWeapon->Variant, (StainId)first.AsWeapon->ModelUnknown), CharacterWeapon.Empty); default: var (main, off) = DetermineMainhand(first, second); - var mainData = new CharacterWeapon(main.AsWeapon->ModelSetId, main.AsWeapon->SecondaryId, main.AsWeapon->Variant, + var mainData = new CharacterWeapon(main.AsWeapon->ModelSetId, main.AsWeapon->SecondaryId, (Variant)main.AsWeapon->Variant, (StainId)main.AsWeapon->ModelUnknown); - var offData = new CharacterWeapon(off.AsWeapon->ModelSetId, off.AsWeapon->SecondaryId, off.AsWeapon->Variant, + var offData = new CharacterWeapon(off.AsWeapon->ModelSetId, off.AsWeapon->SecondaryId, (Variant)off.AsWeapon->Variant, (StainId)off.AsWeapon->ModelUnknown); return (main, off, mainData, offData); } @@ -145,14 +146,14 @@ public readonly unsafe struct Model : IEquatable Model main = *((nint*)&actor.AsCharacter->DrawData.MainHand + 1); var mainData = CharacterWeapon.Empty; if (main.IsWeapon) - mainData = new CharacterWeapon(main.AsWeapon->ModelSetId, main.AsWeapon->SecondaryId, main.AsWeapon->Variant, + mainData = new CharacterWeapon(main.AsWeapon->ModelSetId, main.AsWeapon->SecondaryId, (Variant)main.AsWeapon->Variant, (StainId)main.AsWeapon->ModelUnknown); else main = Null; Model off = *((nint*)&actor.AsCharacter->DrawData.OffHand + 1); var offData = CharacterWeapon.Empty; if (off.IsWeapon) - offData = new CharacterWeapon(off.AsWeapon->ModelSetId, off.AsWeapon->SecondaryId, off.AsWeapon->Variant, + offData = new CharacterWeapon(off.AsWeapon->ModelSetId, off.AsWeapon->SecondaryId, (Variant)off.AsWeapon->Variant, (StainId)off.AsWeapon->ModelUnknown); else off = Null; diff --git a/Glamourer/Interop/VisorService.cs b/Glamourer/Interop/VisorService.cs index d74c286..f6da12d 100644 --- a/Glamourer/Interop/VisorService.cs +++ b/Glamourer/Interop/VisorService.cs @@ -40,7 +40,7 @@ public class VisorService : IDisposable if (oldState == on) return false; - SetupVisorHook(human, human.GetArmor(EquipSlot.Head).Set.Value, on); + SetupVisorHook(human, human.GetArmor(EquipSlot.Head).Set.Id, on); return true; } diff --git a/Glamourer/Interop/WeaponService.cs b/Glamourer/Interop/WeaponService.cs index dec17ea..8873114 100644 --- a/Glamourer/Interop/WeaponService.cs +++ b/Glamourer/Interop/WeaponService.cs @@ -58,7 +58,7 @@ public unsafe class WeaponService : IDisposable _loadWeaponHook.Original(drawData, slot, weapon.Value, redrawOnEquality, unk2, skipGameObject, unk4); if (tmpWeapon.Value != weapon.Value) { - if (tmpWeapon.Set.Value == 0) + if (tmpWeapon.Set.Id == 0) tmpWeapon.Stain = 0; _loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4); } @@ -91,7 +91,7 @@ public unsafe class WeaponService : IDisposable var mdl = character.Model; var (_, _, mh, oh) = mdl.GetWeapons(character); var value = slot == EquipSlot.OffHand ? oh : mh; - var weapon = value.With(value.Set.Value == 0 ? 0 : stain); + var weapon = value.With(value.Set.Id == 0 ? 0 : stain); LoadWeapon(character, slot, weapon); } } diff --git a/Glamourer/Services/ItemManager.cs b/Glamourer/Services/ItemManager.cs index f691c35..ef027eb 100644 --- a/Glamourer/Services/ItemManager.cs +++ b/Glamourer/Services/ItemManager.cs @@ -48,13 +48,13 @@ public class ItemManager : IDisposable public (bool, CharacterArmor) ResolveRestrictedGear(CharacterArmor armor, EquipSlot slot, Race race, Gender gender) => _config.UseRestrictedGearProtection ? RestrictedGear.ResolveRestricted(armor, slot, race, gender) : (false, armor); - public static uint NothingId(EquipSlot slot) + public static ItemId NothingId(EquipSlot slot) => uint.MaxValue - 128 - (uint)slot.ToSlot(); - public static uint SmallclothesId(EquipSlot slot) + public static ItemId SmallclothesId(EquipSlot slot) => uint.MaxValue - 256 - (uint)slot.ToSlot(); - public static uint NothingId(FullEquipType type) + public static ItemId NothingId(FullEquipType type) => uint.MaxValue - 384 - (uint)type; public static EquipItem NothingItem(EquipSlot slot) @@ -66,7 +66,7 @@ public class ItemManager : IDisposable public static EquipItem SmallClothesItem(EquipSlot slot) => new(SmallClothesNpc, SmallclothesId(slot), 0, SmallClothesNpcModel, 0, 1, slot.ToEquipType()); - public EquipItem Resolve(EquipSlot slot, uint itemId) + public EquipItem Resolve(EquipSlot slot, ItemId itemId) { slot = slot.ToSlot(); if (itemId == NothingId(slot)) @@ -83,7 +83,7 @@ public class ItemManager : IDisposable return item; } - public EquipItem Resolve(FullEquipType type, uint itemId) + public EquipItem Resolve(FullEquipType type, ItemId itemId) { if (itemId == NothingId(type)) return NothingItem(type); @@ -97,13 +97,13 @@ public class ItemManager : IDisposable return item; } - public EquipItem Identify(EquipSlot slot, SetId id, byte variant) + public EquipItem Identify(EquipSlot slot, SetId id, Variant variant) { slot = slot.ToSlot(); if (slot.ToIndex() == uint.MaxValue) - return new EquipItem($"Invalid ({id.Value}-{variant})", 0, 0, id, 0, variant, 0); + return new EquipItem($"Invalid ({id.Id}-{variant})", 0, 0, id, 0, variant, 0); - switch (id.Value) + switch (id.Id) { case 0: return NothingItem(slot); case SmallClothesNpcModel: return SmallClothesItem(slot); @@ -125,17 +125,17 @@ public class ItemManager : IDisposable return NothingItem(offhandType); } - public EquipItem Identify(EquipSlot slot, SetId id, WeaponType type, byte variant, FullEquipType mainhandType = FullEquipType.Unknown) + public EquipItem Identify(EquipSlot slot, SetId id, WeaponType type, Variant variant, FullEquipType mainhandType = FullEquipType.Unknown) { if (slot is EquipSlot.OffHand) { var weaponType = mainhandType.ValidOffhand(); - if (id.Value == 0) + if (id.Id == 0) return NothingItem(weaponType); } if (slot is not EquipSlot.MainHand and not EquipSlot.OffHand) - return new EquipItem($"Invalid ({id.Value}-{type.Value}-{variant})", 0, 0, id, type, variant, 0); + return new EquipItem($"Invalid ({id.Id}-{type.Id}-{variant})", 0, 0, id, type, variant, 0); var item = IdentifierService.AwaitedService.Identify(id, type, variant, slot).FirstOrDefault(); return item.Valid @@ -145,7 +145,7 @@ public class ItemManager : IDisposable /// Returns whether an item id represents a valid item for a slot and gives the item. [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public bool IsItemValid(EquipSlot slot, uint itemId, out EquipItem item) + public bool IsItemValid(EquipSlot slot, ItemId itemId, out EquipItem item) { item = Resolve(slot, itemId); return item.Valid; @@ -156,20 +156,19 @@ public class ItemManager : IDisposable /// The returned item is either the resolved correct item, or the Nothing item for that slot. /// The return value is an empty string if there was no problem and a warning otherwise. /// - public string ValidateItem(EquipSlot slot, ulong itemId, out EquipItem item, bool allowUnknown) + public string ValidateItem(EquipSlot slot, CustomItemId itemId, out EquipItem item, bool allowUnknown) { if (slot is EquipSlot.MainHand or EquipSlot.OffHand) throw new Exception("Internal Error: Used armor functionality for weapons."); - if (itemId > uint.MaxValue) + if (!itemId.IsItem) { - var id = (SetId)(itemId & ushort.MaxValue); - var variant = (byte)(itemId >> 32); + var (id, _, variant, _) = itemId.Split; item = Identify(slot, id, variant); return allowUnknown ? string.Empty : $"The item {itemId} yields an unknown item."; } - if (IsItemValid(slot, (uint)itemId, out item)) + if (IsItemValid(slot, itemId.Item, out item)) return string.Empty; item = NothingItem(slot); @@ -179,7 +178,7 @@ public class ItemManager : IDisposable /// Returns whether a stain id is a valid stain. [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public bool IsStainValid(StainId stain) - => stain.Value == 0 || Stains.ContainsKey(stain); + => stain.Id == 0 || Stains.ContainsKey(stain); /// /// Check whether a stain id is an existing stain. @@ -200,7 +199,7 @@ public class ItemManager : IDisposable /// Returns whether an offhand is valid given the required offhand type. [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public bool IsOffhandValid(FullEquipType offType, uint offId, out EquipItem off) + public bool IsOffhandValid(FullEquipType offType, ItemId offId, out EquipItem off) { off = Resolve(offType, offId); return offType == FullEquipType.Unknown || off.Valid; @@ -208,7 +207,7 @@ public class ItemManager : IDisposable /// Returns whether an offhand is valid given mainhand. [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public bool IsOffhandValid(in EquipItem main, uint offId, out EquipItem off) + public bool IsOffhandValid(in EquipItem main, ItemId offId, out EquipItem off) => IsOffhandValid(main.Type.ValidOffhand(), offId, out off); /// @@ -218,7 +217,7 @@ public class ItemManager : IDisposable /// or the default sword and a nothing offhand. /// The return value is an empty string if there was no problem and a warning otherwise. /// - public string ValidateWeapons(uint mainId, uint offId, out EquipItem main, out EquipItem off) + public string ValidateWeapons(ItemId mainId, ItemId offId, out EquipItem main, out EquipItem off) { var ret = string.Empty; if (!IsItemValid(EquipSlot.MainHand, mainId, out main)) diff --git a/Glamourer/Services/TextureService.cs b/Glamourer/Services/TextureService.cs index a8b8ac3..02d080a 100644 --- a/Glamourer/Services/TextureService.cs +++ b/Glamourer/Services/TextureService.cs @@ -21,7 +21,7 @@ public sealed class TextureService : TextureCache, IDisposable public (nint, Vector2, bool) GetIcon(EquipItem item) { - if (item.IconId != 0 && TryLoadIcon(item.IconId, out var ret)) + if (item.IconId.Id != 0 && TryLoadIcon(item.IconId.Id, out var ret)) return (ret.Value.Texture, ret.Value.Dimensions, false); var idx = item.Type.ToSlot().ToIndex(); diff --git a/Glamourer/State/StateApplier.cs b/Glamourer/State/StateApplier.cs index 9cfa129..311bcaf 100644 --- a/Glamourer/State/StateApplier.cs +++ b/Glamourer/State/StateApplier.cs @@ -189,7 +189,7 @@ public class StateApplier /// Apply a weapon to the offhand. public void ChangeOffhand(ActorData data, EquipItem weapon, StainId stain) { - stain = weapon.ModelId.Value == 0 ? 0 : stain; + stain = weapon.ModelId.Id == 0 ? 0 : stain; foreach (var actor in data.Objects.Where(a => a.Model.IsHuman)) _weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain)); } diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 4ae3e0a..01d68c9 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -260,7 +260,7 @@ public class StateListener : IDisposable private void OnWeaponLoading(Actor actor, EquipSlot slot, Ref weapon) { // Fist weapon gauntlet hack. - if (slot is EquipSlot.OffHand && weapon.Value.Variant == 0 && weapon.Value.Set.Value != 0 && _lastFistOffhand.Set.Value != 0) + if (slot is EquipSlot.OffHand && weapon.Value.Variant == 0 && weapon.Value.Set.Id != 0 && _lastFistOffhand.Set.Id != 0) weapon.Value = _lastFistOffhand; if (!actor.Identifier(_actors.AwaitedService, out var identifier) @@ -297,13 +297,13 @@ public class StateListener : IDisposable var newWeapon = state.ModelData.Weapon(slot); if (baseType is FullEquipType.Unknown || baseType == state.ModelData.Item(slot).Type || _gPose.InGPose && actor.IsGPoseOrCutscene) actorWeapon = newWeapon; - else if (actorWeapon.Set.Value != 0) + else if (actorWeapon.Set.Id != 0) actorWeapon = actorWeapon.With(newWeapon.Stain); } // Fist Weapon Offhand hack. - if (slot is EquipSlot.MainHand && weapon.Value.Set.Value is > 1600 and < 1651) - _lastFistOffhand = new CharacterWeapon((SetId)(weapon.Value.Set.Value + 50), weapon.Value.Type, weapon.Value.Variant, + if (slot is EquipSlot.MainHand && weapon.Value.Set.Id is > 1600 and < 1651) + _lastFistOffhand = new CharacterWeapon((SetId)(weapon.Value.Set.Id + 50), weapon.Value.Type, weapon.Value.Variant, weapon.Value.Stain); } @@ -316,7 +316,7 @@ public class StateListener : IDisposable return false; var offhand = actor.GetOffhand(); - return offhand.Variant == 0 && offhand.Set.Value != 0 && armor.Set.Value == offhand.Set.Value; + return offhand.Variant == 0 && offhand.Set.Id != 0 && armor.Set.Id == offhand.Set.Id; } var actorArmor = actor.GetArmor(slot); @@ -333,7 +333,7 @@ public class StateListener : IDisposable change = UpdateState.Change; } - if (baseData.Set.Value != armor.Set.Value || baseData.Variant != armor.Variant) + if (baseData.Set.Id != armor.Set.Id || baseData.Variant != armor.Variant) { var item = _items.Identify(slot, armor.Set, armor.Variant); state.BaseData.SetItem(slot, item); @@ -387,9 +387,9 @@ public class StateListener : IDisposable change = UpdateState.Change; } - if (baseData.Set.Value != weapon.Set.Value || baseData.Type.Value != weapon.Type.Value || baseData.Variant != weapon.Variant) + if (baseData.Set.Id != weapon.Set.Id || baseData.Type.Id != weapon.Type.Id || baseData.Variant != weapon.Variant) { - var item = _items.Identify(slot, weapon.Set, weapon.Type, (byte)weapon.Variant, + var item = _items.Identify(slot, weapon.Set, weapon.Type, weapon.Variant, slot is EquipSlot.OffHand ? state.BaseData.Item(EquipSlot.MainHand).Type : FullEquipType.Unknown); state.BaseData.SetItem(slot, item); change = UpdateState.Change; diff --git a/Glamourer/State/StateManager.cs b/Glamourer/State/StateManager.cs index cde6f16..7dac2f9 100644 --- a/Glamourer/State/StateManager.cs +++ b/Glamourer/State/StateManager.cs @@ -174,8 +174,8 @@ public class StateManager : IReadOnlyDictionary } // Set the weapons regardless of source. - var mainItem = _items.Identify(EquipSlot.MainHand, main.Set, main.Type, (byte)main.Variant); - var offItem = _items.Identify(EquipSlot.OffHand, off.Set, off.Type, (byte)off.Variant, mainItem.Type); + var mainItem = _items.Identify(EquipSlot.MainHand, main.Set, main.Type, main.Variant); + var offItem = _items.Identify(EquipSlot.OffHand, off.Set, off.Type, off.Variant, mainItem.Type); ret.SetItem(EquipSlot.MainHand, mainItem); ret.SetStain(EquipSlot.MainHand, main.Stain); ret.SetItem(EquipSlot.OffHand, offItem); @@ -194,11 +194,11 @@ public class StateManager : IReadOnlyDictionary /// This is hardcoded in the game. private void FistWeaponHack(ref DesignData ret, ref CharacterWeapon mainhand, ref CharacterWeapon offhand) { - if (mainhand.Set.Value is < 1601 or >= 1651) + if (mainhand.Set.Id is < 1601 or >= 1651) return; - var gauntlets = _items.Identify(EquipSlot.Hands, offhand.Set, 0, (byte)offhand.Variant); - offhand.Set = (SetId)(mainhand.Set.Value + 50); + var gauntlets = _items.Identify(EquipSlot.Hands, offhand.Set, 0, offhand.Variant); + offhand.Set = (SetId)(mainhand.Set.Id + 50); offhand.Variant = mainhand.Variant; offhand.Type = mainhand.Type; ret.SetItem(EquipSlot.Hands, gauntlets); @@ -275,7 +275,7 @@ public class StateManager : IReadOnlyDictionary ? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc) : _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc, item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType)); Glamourer.Log.Verbose( - $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}) and its stain from {oldStain.Value} to {stain.Value}. [Affecting {actors.ToLazyString("nothing")}.]"); + $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}) and its stain from {oldStain.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]"); _event.Invoke(type, source, state, actors, (old, item, slot)); _event.Invoke(StateChanged.Type.Stain, source, state, actors, (oldStain, stain, slot)); } @@ -289,7 +289,7 @@ public class StateManager : IReadOnlyDictionary var actors = _applier.ChangeStain(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc); Glamourer.Log.Verbose( - $"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Value} to {stain.Value}. [Affecting {actors.ToLazyString("nothing")}.]"); + $"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]"); _event.Invoke(StateChanged.Type.Stain, source, state, actors, (old, stain, slot)); } diff --git a/Glamourer/Unlocks/ItemUnlockManager.cs b/Glamourer/Unlocks/ItemUnlockManager.cs index 8eee4a4..5431a2f 100644 --- a/Glamourer/Unlocks/ItemUnlockManager.cs +++ b/Glamourer/Unlocks/ItemUnlockManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -12,14 +13,12 @@ using Glamourer.Events; using Glamourer.Services; using Lumina.Excel.GeneratedSheets; using Penumbra.GameData.Enums; -using static OtterGui.Raii.ImRaii; -using static Penumbra.GameData.Files.ShpkFile; +using Penumbra.GameData.Structs; using Cabinet = Lumina.Excel.GeneratedSheets.Cabinet; -using Item = Lumina.Excel.GeneratedSheets.Item; namespace Glamourer.Unlocks; -public class ItemUnlockManager : ISavable, IDisposable +public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary { private readonly SaveService _saveService; private readonly ItemManager _items; @@ -46,10 +45,7 @@ public class ItemUnlockManager : ISavable, IDisposable Cabinet = 0x08, } - public readonly IReadOnlyDictionary Unlockable; - - public IReadOnlyDictionary Unlocked - => _unlocked; + public readonly IReadOnlyDictionary Unlockable; public ItemUnlockManager(SaveService saveService, ItemManager items, ClientState clientState, DataManager gameData, Framework framework, ObjectUnlocked @event, IdentifierService identifier) @@ -104,18 +100,19 @@ public class ItemUnlockManager : ISavable, IDisposable InventoryType.RetainerMarket, }; - private bool AddItem(uint itemId, long time) + private bool AddItem(ItemId itemId, long time) { - itemId = HandleHq(itemId); - if (!_items.ItemService.AwaitedService.TryGetValue(itemId, EquipSlot.MainHand, out var equip) || !_unlocked.TryAdd(equip.ItemId, time)) + itemId = itemId.StripModifiers; + if (!_items.ItemService.AwaitedService.TryGetValue(itemId, EquipSlot.MainHand, out var equip) + || !_unlocked.TryAdd(equip.ItemId.Id, time)) return false; - _event.Invoke(ObjectUnlocked.Type.Item, equip.ItemId, DateTimeOffset.FromUnixTimeMilliseconds(time)); + _event.Invoke(ObjectUnlocked.Type.Item, equip.ItemId.Id, DateTimeOffset.FromUnixTimeMilliseconds(time)); var ident = _identifier.AwaitedService.Identify(equip.ModelId, equip.WeaponType, equip.Variant, equip.Type.ToSlot()); foreach (var item in ident) { - if (_unlocked.TryAdd(item.ItemId, time)) - _event.Invoke(ObjectUnlocked.Type.Item, item.ItemId, DateTimeOffset.FromUnixTimeMilliseconds(time)); + if (_unlocked.TryAdd(item.ItemId.Id, time)) + _event.Invoke(ObjectUnlocked.Type.Item, item.ItemId.Id, DateTimeOffset.FromUnixTimeMilliseconds(time)); } return true; @@ -201,27 +198,28 @@ public class ItemUnlockManager : ISavable, IDisposable Save(); } - public bool IsUnlocked(ulong itemId, out DateTimeOffset time) + public bool IsUnlocked(CustomItemId itemId, out DateTimeOffset time) { // Pseudo items are always unlocked. - if (itemId >= _items.ItemSheet.RowCount) + if (itemId.Id >= _items.ItemSheet.RowCount) { time = DateTimeOffset.MinValue; return true; } - if (_unlocked.TryGetValue((uint) itemId, out var t)) + var id = itemId.Item.Id; + if (_unlocked.TryGetValue(id, out var t)) { time = DateTimeOffset.FromUnixTimeMilliseconds(t); return true; } - if (IsGameUnlocked((uint) itemId)) + if (IsGameUnlocked(id)) { time = DateTimeOffset.UtcNow; - if (_unlocked.TryAdd((uint) itemId, time.ToUnixTimeMilliseconds())) + if (_unlocked.TryAdd(id, time.ToUnixTimeMilliseconds())) { - _event.Invoke(ObjectUnlocked.Type.Item, (uint) itemId, time); + _event.Invoke(ObjectUnlocked.Type.Item, id, time); Save(); } @@ -232,7 +230,7 @@ public class ItemUnlockManager : ISavable, IDisposable return false; } - public unsafe bool IsGameUnlocked(uint itemId) + public unsafe bool IsGameUnlocked(ItemId itemId) { if (Unlockable.TryGetValue(itemId, out var req)) return req.IsUnlocked(this); @@ -253,15 +251,14 @@ public class ItemUnlockManager : ISavable, IDisposable var changes = false; foreach (var (itemId, unlock) in Unlockable) { - if (unlock.IsUnlocked(this) && _unlocked.TryAdd(itemId, time)) + if (unlock.IsUnlocked(this) && _unlocked.TryAdd(itemId.Id, time)) { - _event.Invoke(ObjectUnlocked.Type.Item, itemId, DateTimeOffset.FromUnixTimeMilliseconds(time)); + _event.Invoke(ObjectUnlocked.Type.Item, itemId.Id, DateTimeOffset.FromUnixTimeMilliseconds(time)); changes = true; } } // TODO inventories - if (changes) Save(); } @@ -273,7 +270,7 @@ public class ItemUnlockManager : ISavable, IDisposable => _saveService.DelaySave(this, TimeSpan.FromSeconds(10)); public void Save(StreamWriter writer) - => UnlockDictionaryHelpers.Save(writer, Unlocked); + => UnlockDictionaryHelpers.Save(writer, _unlocked); private void Load() { @@ -285,9 +282,9 @@ public class ItemUnlockManager : ISavable, IDisposable private void OnLogin(object? _, EventArgs _2) => Scan(); - private static Dictionary CreateUnlockData(DataManager gameData, ItemManager items) + private static Dictionary CreateUnlockData(DataManager gameData, ItemManager items) { - var ret = new Dictionary(); + var ret = new Dictionary(); var cabinet = gameData.GetExcelSheet()!; foreach (var row in cabinet) { @@ -338,17 +335,33 @@ public class ItemUnlockManager : ISavable, IDisposable var ident = _identifier.AwaitedService.Identify(equip.ModelId, equip.WeaponType, equip.Variant, equip.Type.ToSlot()); foreach (var item2 in ident) { - if (_unlocked.TryAdd(item2.ItemId, time)) - _event.Invoke(ObjectUnlocked.Type.Item, item2.ItemId, DateTimeOffset.FromUnixTimeMilliseconds(time)); + if (_unlocked.TryAdd(item2.ItemId.Id, time)) + _event.Invoke(ObjectUnlocked.Type.Item, item2.ItemId.Id, DateTimeOffset.FromUnixTimeMilliseconds(time)); } } } - private uint HandleHq(uint itemId) - => itemId switch - { - > 1000000 => itemId - 1000000, - > 500000 => itemId - 500000, - _ => itemId, - }; + public IEnumerator> GetEnumerator() + => _unlocked.Select(kvp => new KeyValuePair(kvp.Key, kvp.Value)).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + public int Count + => _unlocked.Count; + + public bool ContainsKey(ItemId key) + => _unlocked.ContainsKey(key.Id); + + public bool TryGetValue(ItemId key, out long value) + => _unlocked.TryGetValue(key.Id, out value); + + public long this[ItemId key] + => _unlocked[key.Id]; + + public IEnumerable Keys + => _unlocked.Keys.Select(i => (ItemId)i); + + public IEnumerable Values + => _unlocked.Values; } diff --git a/OtterGui b/OtterGui index e3d26f1..03b6b17 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit e3d26f16234a4295bf3c7802d87ce43293c6ffe0 +Subproject commit 03b6b17fee66488fff7f598e444fa99454098767 diff --git a/Penumbra.GameData b/Penumbra.GameData index 5dd2b44..dee0ab3 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit 5dd2b440e69b1725fa214b005b7179f2414a4053 +Subproject commit dee0ab36fac204b9da12d45b66b52e8cfb1fdc08