From 6a4b5fc3b25f4700dcc2799b8666d7b15b21190b Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sun, 2 Oct 2022 23:43:21 +0200 Subject: [PATCH] dupidu --- Glamourer.GameData/GameData.cs | 11 +- Glamourer.GameData/Structs/Item.cs | 3 + .../Customization/CustomizationDrawer.Main.cs | 4 +- .../CustomizationDrawer.Simple.cs | 6 +- .../Gui/Equipment/EquipmentDrawer.Items.cs | 353 ++++++++++++++++++ .../Gui/Equipment/EquipmentDrawer.Main.cs | 143 +++++++ .../Gui/Equipment/EquipmentDrawer.Misc.cs | 95 +++++ .../Gui/Equipment/Interface.Equipment.cs | 225 ----------- Glamourer/Gui/Interface.Actors.cs | 3 + Glamourer/Gui/InterfaceHelpers.cs | 13 - Glamourer/Gui/InterfaceInitialization.cs | 44 +-- Glamourer/Interop/Actor.cs | 41 +- Glamourer/Interop/RedrawManager.cs | 43 ++- Glamourer/State/CharacterSave.cs | 49 +++ Glamourer/State/CurrentDesign.cs | 7 - 15 files changed, 740 insertions(+), 300 deletions(-) create mode 100644 Glamourer/Gui/Equipment/EquipmentDrawer.Items.cs create mode 100644 Glamourer/Gui/Equipment/EquipmentDrawer.Main.cs create mode 100644 Glamourer/Gui/Equipment/EquipmentDrawer.Misc.cs delete mode 100644 Glamourer/Gui/Equipment/Interface.Equipment.cs diff --git a/Glamourer.GameData/GameData.cs b/Glamourer.GameData/GameData.cs index e361b57..dbf1ba4 100644 --- a/Glamourer.GameData/GameData.cs +++ b/Glamourer.GameData/GameData.cs @@ -77,6 +77,7 @@ public static class GameData EmptySlot(EquipSlot.Feet), EmptyNpc(EquipSlot.Feet), }, + [EquipSlot.MainHand] = new(2000), [EquipSlot.RFinger] = new(200) { EmptySlot(EquipSlot.RFinger), @@ -87,8 +88,6 @@ public static class GameData EmptySlot(EquipSlot.Neck), EmptyNpc(EquipSlot.Neck), }, - [EquipSlot.MainHand] = new(1000) { EmptySlot(EquipSlot.MainHand) }, - [EquipSlot.OffHand] = new(200) { EmptySlot(EquipSlot.OffHand) }, [EquipSlot.Wrists] = new(200) { EmptySlot(EquipSlot.Wrists), @@ -112,10 +111,10 @@ public static class GameData continue; slot = slot.ToSlot(); - if (!_itemsBySlot.TryGetValue(slot, out var list)) - continue; - - list.Add(new Item(item, name, slot)); + if (slot == EquipSlot.OffHand) + slot = EquipSlot.MainHand; + if (_itemsBySlot.TryGetValue(slot, out var list)) + list.Add(new Item(item, name, slot)); } foreach (var list in _itemsBySlot.Values) diff --git a/Glamourer.GameData/Structs/Item.cs b/Glamourer.GameData/Structs/Item.cs index 22fe0bb..e952c7f 100644 --- a/Glamourer.GameData/Structs/Item.cs +++ b/Glamourer.GameData/Structs/Item.cs @@ -24,6 +24,9 @@ public readonly struct Item public bool IsBothHand => (EquipSlot)Base.EquipSlotCategory.Row == EquipSlot.BothHand; + public WeaponCategory WeaponCategory + => (WeaponCategory?) Base.ItemUICategory?.Row ?? WeaponCategory.Unknown; + // Create a new item from its sheet list with the given name and either the inferred equip slot or the given one. public Item(Lumina.Excel.GeneratedSheets.Item item, string name, EquipSlot slot = EquipSlot.Unknown) { diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Main.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Main.cs index c471bf7..0418a49 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Main.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Main.cs @@ -142,7 +142,7 @@ internal partial class CustomizationDrawer // Update all relevant Actors by calling the UpdateCustomize game function. private void UpdateActors() { - foreach (var actor in _actors.Where(a => a && a.DrawObject)) - Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize); + foreach (var actor in _actors) + Glamourer.RedrawManager.UpdateCustomize(actor, _customize); } } diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs index 8172051..7aff7dd 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs @@ -25,7 +25,7 @@ internal partial class CustomizationDrawer private void ListCombo() { ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale); - using var combo = ImRaii.Combo("##combo", $"{_currentOption} #{_currentByte.Value + 1}"); + using var combo = ImRaii.Combo("##combo", $"{_currentOption} #{_currentByte.Value + 1}"); if (!combo) return; @@ -83,8 +83,7 @@ internal partial class CustomizationDrawer if (ImGui.Checkbox(label, ref tmp) && tmp != current) { setter(tmp); - foreach (var actor in _actors.Where(a => a && a.DrawObject)) - Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize); + UpdateActors(); } } @@ -99,6 +98,7 @@ internal partial class CustomizationDrawer var data = _set.Data(_currentId, currentIndex, _customize.Face); UpdateValue(data.Value); } + ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]"); } } diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.Items.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.Items.cs new file mode 100644 index 0000000..26347c0 --- /dev/null +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.Items.cs @@ -0,0 +1,353 @@ +using System; +using System.Collections.Generic; +using Dalamud.Interface; +using ImGuiNET; +using Lumina.Excel.GeneratedSheets; +using Lumina.Text; +using OtterGui; +using OtterGui.Classes; +using OtterGui.Widgets; +using Penumbra.GameData; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; +using Item = Glamourer.Structs.Item; + +namespace Glamourer.Gui.Equipment; + +public partial class EquipmentDrawer +{ + public const int ItemComboWidth = 320; + + private sealed class ItemCombo : FilterComboBase + { + public readonly string Label; + public readonly EquipSlot Slot; + + public Lumina.Excel.GeneratedSheets.Item? LastItem; + private CharacterArmor _lastArmor; + private string _lastPreview = string.Empty; + private int _lastIndex; + + public ItemCombo(EquipSlot slot) + : base(GetItems(slot), false) + { + Label = GetLabel(slot); + Slot = slot; + } + + protected override string ToString(Item obj) + => obj.Name; + + private static string GetLabel(EquipSlot slot) + { + var sheet = Dalamud.GameData.GetExcelSheet()!; + + return slot switch + { + EquipSlot.Head => sheet.GetRow(740)?.Text.ToString() ?? "Head", + EquipSlot.Body => sheet.GetRow(741)?.Text.ToString() ?? "Body", + EquipSlot.Hands => sheet.GetRow(742)?.Text.ToString() ?? "Hands", + EquipSlot.Legs => sheet.GetRow(744)?.Text.ToString() ?? "Legs", + EquipSlot.Feet => sheet.GetRow(745)?.Text.ToString() ?? "Feet", + EquipSlot.Ears => sheet.GetRow(746)?.Text.ToString() ?? "Ears", + EquipSlot.Neck => sheet.GetRow(747)?.Text.ToString() ?? "Neck", + EquipSlot.Wrists => sheet.GetRow(748)?.Text.ToString() ?? "Wrists", + EquipSlot.RFinger => sheet.GetRow(749)?.Text.ToString() ?? "Right Ring", + EquipSlot.LFinger => sheet.GetRow(750)?.Text.ToString() ?? "Left Ring", + _ => string.Empty, + }; + } + + public bool Draw(CharacterArmor armor, out int newIdx) + { + UpdateItem(armor); + newIdx = _lastIndex; + return Draw(Label, _lastPreview, ref newIdx, ItemComboWidth * ImGuiHelpers.GlobalScale, ImGui.GetTextLineHeight()); + } + + private void UpdateItem(CharacterArmor armor) + { + if (armor.Equals(_lastArmor)) + return; + + _lastArmor = armor; + LastItem = Identify(armor.Set, 0, armor.Variant, Slot); + _lastIndex = Items.IndexOf(i => i.Base.RowId == LastItem.RowId); + _lastPreview = _lastIndex >= 0 ? Items[_lastIndex].Name : LastItem.Name.ToString(); + } + + private static IReadOnlyList GetItems(EquipSlot slot) + => GameData.ItemsBySlot(Dalamud.GameData).TryGetValue(slot, out var list) ? list : Array.Empty(); + } + + private sealed class WeaponCombo : FilterComboBase + { + public readonly string Label; + public readonly EquipSlot Slot; + + public Lumina.Excel.GeneratedSheets.Item? LastItem; + private CharacterWeapon _lastWeapon = new(ulong.MaxValue); + private string _lastPreview = string.Empty; + private int _lastIndex; + public WeaponCategory LastCategory { get; private set; } + private bool _drawAll; + + public WeaponCombo(EquipSlot slot) + : base(GetItems(slot), false) + { + Label = GetLabel(slot); + Slot = slot; + } + + protected override string ToString(Item obj) + => obj.Name; + + private static string GetLabel(EquipSlot slot) + { + var sheet = Dalamud.GameData.GetExcelSheet()!; + return slot switch + { + EquipSlot.MainHand => sheet.GetRow(738)?.Text.ToString() ?? "Main Hand", + EquipSlot.OffHand => sheet.GetRow(739)?.Text.ToString() ?? "Off Hand", + _ => string.Empty, + }; + } + + public bool Draw(CharacterWeapon weapon, out int newIdx, bool drawAll) + { + if (drawAll != _drawAll) + { + _drawAll = drawAll; + ResetFilter(); + } + + UpdateItem(weapon); + UpdateCategory((WeaponCategory?)LastItem!.ItemUICategory?.Row ?? WeaponCategory.Unknown); + newIdx = _lastIndex; + return Draw(Label, _lastPreview, ref newIdx, ItemComboWidth * ImGuiHelpers.GlobalScale, ImGui.GetTextLineHeight()); + } + + public bool Draw(CharacterWeapon weapon, WeaponCategory category, out int newIdx) + { + if (_drawAll) + { + _drawAll = false; + ResetFilter(); + } + + UpdateItem(weapon); + UpdateCategory(category); + newIdx = _lastIndex; + return Draw(Label, _lastPreview, ref newIdx, ItemComboWidth * ImGuiHelpers.GlobalScale, ImGui.GetTextLineHeight()); + } + + protected override bool IsVisible(int globalIndex, LowerString filter) + { + var item = Items[globalIndex]; + return (_drawAll || item.WeaponCategory == LastCategory) && filter.IsContained(item.Name); + } + + private void UpdateItem(CharacterWeapon weapon) + { + if (weapon.Equals(_lastWeapon)) + return; + + _lastWeapon = weapon; + LastItem = Identify(weapon.Set, weapon.Type, weapon.Variant, Slot); + _lastIndex = LastItem.RowId == 0 ? -1 : Items.IndexOf(i => i.Base.RowId == LastItem.RowId); + _lastPreview = _lastIndex >= 0 ? Items[_lastIndex].Name : LastItem.Name.ToString(); + } + + private void UpdateCategory(WeaponCategory category) + { + if (category == LastCategory) + return; + + LastCategory = category; + ResetFilter(); + } + + private static IReadOnlyList GetItems(EquipSlot slot) + => GameData.ItemsBySlot(Dalamud.GameData).TryGetValue(EquipSlot.MainHand, out var list) ? list : Array.Empty(); + } + + private static readonly IObjectIdentifier Identifier; + private static readonly IReadOnlyList ItemCombos; + private static readonly WeaponCombo MainHandCombo; + private static readonly WeaponCombo OffHandCombo; + + private void DrawItemSelector() + { + var combo = ItemCombos[(int)_currentSlotIdx]; + var change = combo.Draw(_currentArmor, out var idx); + var newItem = change ? ToArmor(combo.Items[idx], _currentArmor.Stain) : CharacterArmor.Empty; + if (!change && !ReferenceEquals(combo.LastItem, SmallClothes)) + { + ImGuiUtil.HoverTooltip("Right-click to clear."); + if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) + change = true; + } + + if (!change) + return; + + _currentArmor = newItem; + UpdateActors(); + } + + private static CharacterArmor ToArmor(Item item, StainId stain) + { + var (id, _, variant) = item.MainModel; + return new CharacterArmor(id, (byte)variant, stain); + } + + private static CharacterWeapon ToWeapon(Item item, StainId stain) + { + var (id, type, variant) = item.MainModel; + return new CharacterWeapon(id, type, variant, stain); + } + + private void DrawMainHandSelector(ref CharacterWeapon mainHand) + { + if (!MainHandCombo.Draw(mainHand, out var newIdx, false)) + return; + + mainHand = ToWeapon(MainHandCombo.Items[newIdx], mainHand.Stain); + foreach (var actor in _actors) + Glamourer.RedrawManager.LoadWeapon(actor, _currentSlot, mainHand); + } + + private void DrawOffHandSelector(ref CharacterWeapon offHand, WeaponCategory category) + { + var change = OffHandCombo.Draw(offHand, category, out var newIdx); + var newWeapon = change ? ToWeapon(OffHandCombo.Items[newIdx], offHand.Stain) : CharacterWeapon.Empty; + if (!change && !ReferenceEquals(OffHandCombo.LastItem, SmallClothes)) + { + ImGuiUtil.HoverTooltip("Right-click to clear."); + if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) + change = true; + } + + if (!change) + return; + + offHand = newWeapon; + foreach (var actor in _actors) + Glamourer.RedrawManager.LoadWeapon(actor, _currentSlot, offHand); + } + //private bool DrawEquipSlot(EquipSlot slot, CharacterArmor equip) + //{ + // var (equipCombo, stainCombo) = _combos[slot]; + // + // var ret = DrawStainSelector(stainCombo, slot, equip.Stain); + // ImGui.SameLine(); + // var item = Identify(equip.Set, new WeaponType(), equip.Variant, slot); + // ret |= DrawItemSelector(equipCombo, item, slot); + // + // return ret; + //} + // + //private bool DrawEquipSlotWithCheck(EquipSlot slot, CharacterArmor equip, CharacterEquipMask flag, ref CharacterEquipMask mask) + //{ + // var ret = DrawCheckbox(flag, ref mask); + // ImGui.SameLine(); + // ret |= DrawEquipSlot(slot, equip); + // return ret; + //} + // + //private bool DrawWeapon(EquipSlot slot, CharacterWeapon weapon) + //{ + // var (equipCombo, stainCombo) = _combos[slot]; + // + // var ret = DrawStainSelector(stainCombo, slot, weapon.Stain); + // ImGui.SameLine(); + // var item = Identify(weapon.Set, weapon.Type, weapon.Variant, slot); + // ret |= DrawItemSelector(equipCombo, item, slot); + // + // return ret; + //} + // + //private bool DrawWeaponWithCheck(EquipSlot slot, CharacterWeapon weapon, CharacterEquipMask flag, ref CharacterEquipMask mask) + //{ + // var ret = DrawCheckbox(flag, ref mask); + // ImGui.SameLine(); + // ret |= DrawWeapon(slot, weapon); + // return ret; + //} + // + //private bool DrawEquip(CharacterEquipment equip) + //{ + // var ret = false; + // if (ImGui.CollapsingHeader("Character Equipment")) + // { + // ret |= DrawWeapon(EquipSlot.MainHand, equip.MainHand); + // ret |= DrawWeapon(EquipSlot.OffHand, equip.OffHand); + // ret |= DrawEquipSlot(EquipSlot.Head, equip.Head); + // ret |= DrawEquipSlot(EquipSlot.Body, equip.Body); + // ret |= DrawEquipSlot(EquipSlot.Hands, equip.Hands); + // ret |= DrawEquipSlot(EquipSlot.Legs, equip.Legs); + // ret |= DrawEquipSlot(EquipSlot.Feet, equip.Feet); + // ret |= DrawEquipSlot(EquipSlot.Ears, equip.Ears); + // ret |= DrawEquipSlot(EquipSlot.Neck, equip.Neck); + // ret |= DrawEquipSlot(EquipSlot.Wrists, equip.Wrists); + // ret |= DrawEquipSlot(EquipSlot.RFinger, equip.RFinger); + // ret |= DrawEquipSlot(EquipSlot.LFinger, equip.LFinger); + // } + // + // return ret; + //} + // + //private bool DrawEquip(CharacterEquipment equip, ref CharacterEquipMask mask) + //{ + // var ret = false; + // if (ImGui.CollapsingHeader("Character Equipment")) + // { + // ret |= DrawWeaponWithCheck(EquipSlot.MainHand, equip.MainHand, CharacterEquipMask.MainHand, ref mask); + // ret |= DrawWeaponWithCheck(EquipSlot.OffHand, equip.OffHand, CharacterEquipMask.OffHand, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.Head, equip.Head, CharacterEquipMask.Head, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.Body, equip.Body, CharacterEquipMask.Body, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.Hands, equip.Hands, CharacterEquipMask.Hands, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.Legs, equip.Legs, CharacterEquipMask.Legs, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.Feet, equip.Feet, CharacterEquipMask.Feet, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.Ears, equip.Ears, CharacterEquipMask.Ears, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.Neck, equip.Neck, CharacterEquipMask.Neck, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.Wrists, equip.Wrists, CharacterEquipMask.Wrists, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.RFinger, equip.RFinger, CharacterEquipMask.RFinger, ref mask); + // ret |= DrawEquipSlotWithCheck(EquipSlot.LFinger, equip.LFinger, CharacterEquipMask.LFinger, ref mask); + // } + // + // return ret; + //} + // + // + // + + + private static readonly Lumina.Excel.GeneratedSheets.Item SmallClothes = new() + { + Name = new SeString("Nothing"), + RowId = 0, + }; + + private static readonly Lumina.Excel.GeneratedSheets.Item SmallClothesNpc = new() + { + Name = new SeString("Smallclothes (NPC)"), + RowId = 1, + }; + + private static readonly Lumina.Excel.GeneratedSheets.Item Unknown = new() + { + Name = new SeString("Unknown"), + RowId = 2, + }; + + private static Lumina.Excel.GeneratedSheets.Item Identify(SetId set, WeaponType weapon, ushort variant, EquipSlot slot) + { + return (uint)set switch + { + 0 => SmallClothes, + 9903 => SmallClothesNpc, + _ => Identifier.Identify(set, weapon, variant, slot) ?? Unknown, + }; + } +} diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.Main.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.Main.cs new file mode 100644 index 0000000..9f6837a --- /dev/null +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.Main.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Glamourer.Customization; +using Glamourer.Interop; +using Glamourer.State; +using ImGuiNET; +using OtterGui.Raii; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; + +namespace Glamourer.Gui.Equipment; + +public partial class EquipmentDrawer +{ + private Race _race; + private Gender _gender; + private CharacterEquip _equip; + private IReadOnlyCollection _actors = Array.Empty(); + + private CharacterArmor _currentArmor; + private EquipSlot _currentSlot; + private uint _currentSlotIdx; + + static EquipmentDrawer() + { + Stains = GameData.Stains(Dalamud.GameData); + StainCombo = new FilterStainCombo(140); + Identifier = Penumbra.GameData.GameData.GetIdentifier(Dalamud.GameData); + ItemCombos = EquipSlotExtensions.EqdpSlots.Select(s => new ItemCombo(s)).ToArray(); + MainHandCombo = new WeaponCombo(EquipSlot.MainHand); + OffHandCombo = new WeaponCombo(EquipSlot.OffHand); + } + + public static void Draw(Customize customize, CharacterEquip equip, ref CharacterWeapon mainHand, ref CharacterWeapon offHand, + IReadOnlyCollection actors, bool locked) + { + var d = new EquipmentDrawer() + { + _race = customize.Race, + _gender = customize.Gender, + _equip = equip, + _actors = actors, + }; + + if (!ImGui.CollapsingHeader("Character Equipment")) + return; + + using var disabled = ImRaii.Disabled(locked); + + d.DrawInternal(ref mainHand, ref offHand); + } + + + public static void Draw(Customize customize, CharacterEquip equip, ref CharacterWeapon mainHand, ref CharacterWeapon offHand, + ref ApplicationFlags flags, IReadOnlyCollection actors, + bool locked) + { + var d = new EquipmentDrawer() + { + _race = customize.Race, + _gender = customize.Gender, + _equip = equip, + _actors = actors, + }; + + if (!ImGui.CollapsingHeader("Character Equipment")) + return; + + using var disabled = ImRaii.Disabled(locked); + + d.DrawInternal(ref mainHand, ref offHand, ref flags); + } + + private void DrawInternal(ref CharacterWeapon mainHand, ref CharacterWeapon offHand) + { + foreach (var slot in EquipSlotExtensions.EqdpSlots) + { + using var id = SetSlot(slot); + DrawStainSelector(); + ImGui.SameLine(); + DrawItemSelector(); + } + + _currentSlot = EquipSlot.MainHand; + DrawStainSelector(); + ImGui.SameLine(); + DrawMainHandSelector(ref mainHand); + var offhand = MainHandCombo.LastCategory.AllowsOffHand(); + if (offhand != WeaponCategory.Unknown) + { + _currentSlot = EquipSlot.OffHand; + DrawStainSelector(); + ImGui.SameLine(); + DrawOffHandSelector(ref offHand, offhand); + } + } + + private void DrawInternal(ref CharacterWeapon mainHand, ref CharacterWeapon offHand, ref ApplicationFlags flags) + { + foreach (var slot in EquipSlotExtensions.EqdpSlots) + { + using var id = SetSlot(slot); + DrawCheckbox(ref flags); + ImGui.SameLine(); + DrawStainSelector(); + ImGui.SameLine(); + DrawItemSelector(); + } + + _currentSlot = EquipSlot.MainHand; + DrawCheckbox(ref flags); + ImGui.SameLine(); + DrawStainSelector(); + ImGui.SameLine(); + DrawMainHandSelector(ref mainHand); + var offhand = MainHandCombo.LastCategory.AllowsOffHand(); + if (offhand != WeaponCategory.Unknown) + { + _currentSlot = EquipSlot.OffHand; + DrawCheckbox(ref flags); + ImGui.SameLine(); + DrawStainSelector(); + ImGui.SameLine(); + DrawOffHandSelector(ref offHand, offhand); + } + } + + private ImRaii.Id SetSlot(EquipSlot slot) + { + _currentSlot = slot; + _currentSlotIdx = slot.ToIndex(); + _currentArmor = _equip[slot]; + return ImRaii.PushId((int)slot); + } + + private void UpdateActors() + { + _equip[_currentSlotIdx] = _currentArmor; + foreach (var actor in _actors) + Glamourer.RedrawManager.ChangeEquip(actor, _currentSlotIdx, _currentArmor); + } +} diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.Misc.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.Misc.cs new file mode 100644 index 0000000..3139a71 --- /dev/null +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.Misc.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Dalamud.Interface; +using Glamourer.State; +using ImGuiNET; +using OtterGui; +using OtterGui.Raii; +using OtterGui.Widgets; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; +using Stain = Glamourer.Structs.Stain; + +namespace Glamourer.Gui.Equipment; + +public partial class EquipmentDrawer +{ + private static readonly IReadOnlyDictionary Stains; + private static readonly FilterStainCombo StainCombo; + + private sealed class FilterStainCombo : FilterComboBase + { + private readonly float _comboWidth; + private Vector2 _buttonSize; + + public FilterStainCombo(float comboWidth) + : base(Stains.Values.ToArray(), false) + => _comboWidth = comboWidth; + + protected override float GetFilterWidth() + => _buttonSize.X + ImGui.GetStyle().ScrollbarSize; + + protected override void DrawList(float width, float itemHeight) + { + using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero) + .Push(ImGuiStyleVar.WindowPadding, Vector2.Zero) + .Push(ImGuiStyleVar.FrameRounding, 0); + _buttonSize = new Vector2(_comboWidth * ImGuiHelpers.GlobalScale, 0); + if (ImGui.GetScrollMaxY() > 0) + _buttonSize.X += ImGui.GetStyle().ScrollbarSize; + base.DrawList(width, itemHeight); + } + + protected override string ToString(Stain obj) + => obj.Name; + + protected override bool DrawSelectable(int globalIdx, bool selected) + { + var stain = Items[globalIdx]; + // Push the stain color to type and if it is too bright, turn the text color black. + using var colors = ImRaii.PushColor(ImGuiCol.Button, stain.RgbaColor) + .Push(ImGuiCol.Text, 0xFF101010, stain.Intensity > 127) + .Push(ImGuiCol.Border, 0xFF2020D0, selected); + using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2f * ImGuiHelpers.GlobalScale, selected); + return ImGui.Button(stain.Name, _buttonSize); + } + } + + private void DrawStainSelector() + { + var foundIdx = StainCombo.Items.IndexOf(s => s.RowIndex.Equals(_currentArmor.Stain)); + var stain = foundIdx >= 0 ? StainCombo.Items[foundIdx] : default; + using var color = ImRaii.PushColor(ImGuiCol.FrameBg, stain.RgbaColor, foundIdx >= 0); + var change = StainCombo.Draw("##stainSelector", string.Empty, ref foundIdx, ImGui.GetFrameHeight(), ImGui.GetFrameHeight(), + ImGuiComboFlags.NoArrowButton); + if (!change && (byte)_currentArmor.Stain != 0) + { + ImGuiUtil.HoverTooltip($"{stain.Name}\nRight-click to clear."); + if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) + { + change = true; + foundIdx = -1; + } + } + + if (change) + { + _currentArmor = new CharacterArmor(_currentArmor.Set, _currentArmor.Variant, + foundIdx >= 0 ? StainCombo.Items[foundIdx].RowIndex : Stain.None.RowIndex); + UpdateActors(); + } + } + + private void DrawCheckbox(ref ApplicationFlags flags) + => DrawCheckbox("##checkbox", "Enable writing this slot in this save.", ref flags, _currentSlot.ToApplicationFlag()); + + private static void DrawCheckbox(string label, string tooltip, ref ApplicationFlags flags, ApplicationFlags flag) + { + var tmp = (uint)flags; + if (ImGui.CheckboxFlags(label, ref tmp, (uint)flag)) + flags = (ApplicationFlags)tmp; + + ImGuiUtil.HoverTooltip(tooltip); + } +} diff --git a/Glamourer/Gui/Equipment/Interface.Equipment.cs b/Glamourer/Gui/Equipment/Interface.Equipment.cs deleted file mode 100644 index 2b56e92..0000000 --- a/Glamourer/Gui/Equipment/Interface.Equipment.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System; -using System.Collections.Generic; -using Glamourer.Customization; -using Glamourer.Interop; -using Glamourer.Structs; -using ImGuiNET; -using Lumina.Text; -using OtterGui; -using OtterGui.Raii; -using Penumbra.GameData.Enums; -using Penumbra.GameData.Structs; - -namespace Glamourer.Gui; - -internal partial class Interface -{ - //public class EquipmentDrawer - //{ - // private static readonly IReadOnlyDictionary Stains; - // - // private Race _race; - // private Gender _gender; - // private CharacterEquip _equip; - // private IReadOnlyCollection _actors = Array.Empty(); - // - // static EquipmentDrawer() - // => Stains = GameData.Stains(Dalamud.GameData); - // - // public static void Draw(Customize customize, CharacterEquip equip, IReadOnlyCollection actors, bool locked) - // { - // var d = new EquipmentDrawer() - // { - // _race = customize.Race, - // _gender = customize.Gender, - // _actors = actors, - // }; - // - // if (!ImGui.CollapsingHeader("Character Equipment")) - // return; - // - // using var disabled = ImRaii.Disabled(locked); - // } - // - // private bool DrawStainSelector(ComboWithFilter stainCombo, EquipSlot slot, StainId stainIdx) - // { - // stainCombo.PostPreview = null; - // var found = Stains.TryGetValue(stainIdx, out var stain); - // using var color = ImRaii.PushColor(ImGuiCol.FrameBg, stain.RgbaColor, found); - // var change = stainCombo.Draw(string.Empty, out var newStain) && !newStain.RowIndex.Equals(stainIdx); - // if () - // if (!change && (byte)stainIdx != 0) - // { - // ImGuiUtil.HoverTooltip("Right-click to clear."); - // if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) - // { - // change = true; - // newStain = Stain.None; - // } - // } - // - // if (!change) - // return false; - // - // if (_player == null) - // return _inDesignMode && (_selection?.Data.WriteStain(slot, newStain.RowIndex) ?? false); - // - // Glamourer.RevertableDesigns.Add(_player); - // newStain.Write(_player.Address, slot); - // return true; - // } - // - // private bool DrawItemSelector(ComboWithFilter equipCombo, Lumina.Excel.GeneratedSheets.Item item, EquipSlot slot = EquipSlot.Unknown) - // { - // var currentName = item.Name.ToString(); - // var change = equipCombo.Draw(currentName, out var newItem, _itemComboWidth) && newItem.Base.RowId != item.RowId; - // if (!change && !ReferenceEquals(item, SmallClothes)) - // { - // ImGuiUtil.HoverTooltip("Right-click to clear."); - // if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) - // { - // change = true; - // newItem = Item.Nothing(slot); - // } - // } - // - // if (!change) - // return false; - // - // newItem = new Item(newItem.Base, newItem.Name, slot); - // if (_player == null) - // return _inDesignMode && (_selection?.Data.WriteItem(newItem) ?? false); - // - // Glamourer.RevertableDesigns.Add(_player); - // newItem.Write(_player.Address); - // return true; - // } - // - // private static bool DrawCheckbox(CharacterEquipMask flag, ref CharacterEquipMask mask) - // { - // var tmp = (uint)mask; - // var ret = false; - // if (ImGui.CheckboxFlags($"##flag_{(uint)flag}", ref tmp, (uint)flag) && tmp != (uint)mask) - // { - // mask = (CharacterEquipMask)tmp; - // ret = true; - // } - // - // if (ImGui.IsItemHovered()) - // ImGui.SetTooltip("Enable writing this slot in this save."); - // return ret; - // } - // - // private static readonly Lumina.Excel.GeneratedSheets.Item SmallClothes = new() - // { - // Name = new SeString("Nothing"), - // RowId = 0, - // }; - // - // private static readonly Lumina.Excel.GeneratedSheets.Item SmallClothesNpc = new() - // { - // Name = new SeString("Smallclothes (NPC)"), - // RowId = 1, - // }; - // - // private static readonly Lumina.Excel.GeneratedSheets.Item Unknown = new() - // { - // Name = new SeString("Unknown"), - // RowId = 2, - // }; - // - // private Lumina.Excel.GeneratedSheets.Item Identify(SetId set, WeaponType weapon, ushort variant, EquipSlot slot) - // { - // return (uint)set switch - // { - // 0 => SmallClothes, - // 9903 => SmallClothesNpc, - // _ => _identifier.Identify(set, weapon, variant, slot) ?? Unknown, - // }; - // } - // - // private bool DrawEquipSlot(EquipSlot slot, CharacterArmor equip) - // { - // var (equipCombo, stainCombo) = _combos[slot]; - // - // var ret = DrawStainSelector(stainCombo, slot, equip.Stain); - // ImGui.SameLine(); - // var item = Identify(equip.Set, new WeaponType(), equip.Variant, slot); - // ret |= DrawItemSelector(equipCombo, item, slot); - // - // return ret; - // } - // - // private bool DrawEquipSlotWithCheck(EquipSlot slot, CharacterArmor equip, CharacterEquipMask flag, ref CharacterEquipMask mask) - // { - // var ret = DrawCheckbox(flag, ref mask); - // ImGui.SameLine(); - // ret |= DrawEquipSlot(slot, equip); - // return ret; - // } - // - // private bool DrawWeapon(EquipSlot slot, CharacterWeapon weapon) - // { - // var (equipCombo, stainCombo) = _combos[slot]; - // - // var ret = DrawStainSelector(stainCombo, slot, weapon.Stain); - // ImGui.SameLine(); - // var item = Identify(weapon.Set, weapon.Type, weapon.Variant, slot); - // ret |= DrawItemSelector(equipCombo, item, slot); - // - // return ret; - // } - // - // private bool DrawWeaponWithCheck(EquipSlot slot, CharacterWeapon weapon, CharacterEquipMask flag, ref CharacterEquipMask mask) - // { - // var ret = DrawCheckbox(flag, ref mask); - // ImGui.SameLine(); - // ret |= DrawWeapon(slot, weapon); - // return ret; - // } - // - // private bool DrawEquip(CharacterEquipment equip) - // { - // var ret = false; - // if (ImGui.CollapsingHeader("Character Equipment")) - // { - // ret |= DrawWeapon(EquipSlot.MainHand, equip.MainHand); - // ret |= DrawWeapon(EquipSlot.OffHand, equip.OffHand); - // ret |= DrawEquipSlot(EquipSlot.Head, equip.Head); - // ret |= DrawEquipSlot(EquipSlot.Body, equip.Body); - // ret |= DrawEquipSlot(EquipSlot.Hands, equip.Hands); - // ret |= DrawEquipSlot(EquipSlot.Legs, equip.Legs); - // ret |= DrawEquipSlot(EquipSlot.Feet, equip.Feet); - // ret |= DrawEquipSlot(EquipSlot.Ears, equip.Ears); - // ret |= DrawEquipSlot(EquipSlot.Neck, equip.Neck); - // ret |= DrawEquipSlot(EquipSlot.Wrists, equip.Wrists); - // ret |= DrawEquipSlot(EquipSlot.RFinger, equip.RFinger); - // ret |= DrawEquipSlot(EquipSlot.LFinger, equip.LFinger); - // } - // - // return ret; - // } - // - // private bool DrawEquip(CharacterEquipment equip, ref CharacterEquipMask mask) - // { - // var ret = false; - // if (ImGui.CollapsingHeader("Character Equipment")) - // { - // ret |= DrawWeaponWithCheck(EquipSlot.MainHand, equip.MainHand, CharacterEquipMask.MainHand, ref mask); - // ret |= DrawWeaponWithCheck(EquipSlot.OffHand, equip.OffHand, CharacterEquipMask.OffHand, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.Head, equip.Head, CharacterEquipMask.Head, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.Body, equip.Body, CharacterEquipMask.Body, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.Hands, equip.Hands, CharacterEquipMask.Hands, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.Legs, equip.Legs, CharacterEquipMask.Legs, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.Feet, equip.Feet, CharacterEquipMask.Feet, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.Ears, equip.Ears, CharacterEquipMask.Ears, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.Neck, equip.Neck, CharacterEquipMask.Neck, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.Wrists, equip.Wrists, CharacterEquipMask.Wrists, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.RFinger, equip.RFinger, CharacterEquipMask.RFinger, ref mask); - // ret |= DrawEquipSlotWithCheck(EquipSlot.LFinger, equip.LFinger, CharacterEquipMask.LFinger, ref mask); - // } - // - // return ret; - // } - //} -} diff --git a/Glamourer/Gui/Interface.Actors.cs b/Glamourer/Gui/Interface.Actors.cs index 828ecfc..3f6ea55 100644 --- a/Glamourer/Gui/Interface.Actors.cs +++ b/Glamourer/Gui/Interface.Actors.cs @@ -3,6 +3,7 @@ using System.Numerics; using Dalamud.Interface; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Glamourer.Gui.Customization; +using Glamourer.Gui.Equipment; using Glamourer.Interop; using Glamourer.State; using ImGuiNET; @@ -60,6 +61,8 @@ internal partial class Interface CustomizationDrawer.Draw(_currentSave.Data.Customize, _currentSave.Data.Equipment, _currentData.Objects, _identifier is Actor.SpecialIdentifier); + + EquipmentDrawer.Draw(_currentSave.Data.Customize, _currentSave.Data.Equipment, ref _currentSave.Data.MainHand, ref _currentSave.Data.OffHand, _currentData.Objects, _identifier is Actor.SpecialIdentifier); } private const uint RedHeaderColor = 0xFF1818C0; diff --git a/Glamourer/Gui/InterfaceHelpers.cs b/Glamourer/Gui/InterfaceHelpers.cs index 7440452..6caf1b9 100644 --- a/Glamourer/Gui/InterfaceHelpers.cs +++ b/Glamourer/Gui/InterfaceHelpers.cs @@ -10,19 +10,6 @@ namespace Glamourer.Gui; //internal partial class Interface //{ -// // Push the stain color to type and if it is too bright, turn the text color black. -// // Return number of pushed styles. -// private static int PushColor(Stain stain, ImGuiCol type = ImGuiCol.Button) -// { -// ImGui.PushStyleColor(type, stain.RgbaColor); -// if (stain.Intensity > 127) -// { -// ImGui.PushStyleColor(ImGuiCol.Text, 0xFF101010); -// return 2; -// } -// -// return 1; -// } // // diff --git a/Glamourer/Gui/InterfaceInitialization.cs b/Glamourer/Gui/InterfaceInitialization.cs index e81d3dc..b4b515b 100644 --- a/Glamourer/Gui/InterfaceInitialization.cs +++ b/Glamourer/Gui/InterfaceInitialization.cs @@ -17,28 +17,28 @@ namespace Glamourer.Gui; // // private static readonly Vector4 GreyVector = new(0.5f, 0.5f, 0.5f, 1); // -// private static ComboWithFilter CreateDefaultStainCombo(IReadOnlyList stains) -// => new("##StainCombo", ColorComboWidth, ColorButtonWidth, stains, -// s => s.Name.ToString()) -// { -// Flags = ImGuiComboFlags.NoArrowButton | ImGuiComboFlags.HeightLarge, -// PreList = () => -// { -// ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); -// ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); -// ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0); -// }, -// PostList = () => { ImGui.PopStyleVar(3); }, -// CreateSelectable = s => -// { -// var push = PushColor(s); -// var ret = ImGui.Button($"{s.Name}##Stain{(byte)s.RowIndex}", -// Vector2.UnitX * (ColorComboWidth - ImGui.GetStyle().ScrollbarSize)); -// ImGui.PopStyleColor(push); -// return ret; -// }, -// ItemsAtOnce = 12, -// }; +// private static ComboWithFilter CreateDefaultStainCombo(IReadOnlyList stains) +// => new("##StainCombo", ColorComboWidth, ColorButtonWidth, stains, +// s => s.Name.ToString()) +// { +// Flags = ImGuiComboFlags.NoArrowButton | ImGuiComboFlags.HeightLarge, +// PreList = () => +// { +// ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); +// ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero); +// ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0); +// }, +// PostList = () => { ImGui.PopStyleVar(3); }, +// CreateSelectable = s => +// { +// var push = PushColor(s); +// var ret = ImGui.Button($"{s.Name}##Stain{(byte)s.RowIndex}", +// Vector2.UnitX * (ColorComboWidth - ImGui.GetStyle().ScrollbarSize)); +// ImGui.PopStyleColor(push); +// return ret; +// }, +// ItemsAtOnce = 12, +// }; // // private ComboWithFilter CreateItemCombo(EquipSlot slot, IReadOnlyList items) // => new($"{_equipSlotNames[slot]}##Equip", ItemComboWidth, ItemComboWidth, items, i => i.Name) diff --git a/Glamourer/Interop/Actor.cs b/Glamourer/Interop/Actor.cs index 214ac52..c2c4977 100644 --- a/Glamourer/Interop/Actor.cs +++ b/Glamourer/Interop/Actor.cs @@ -48,11 +48,33 @@ public unsafe partial struct DrawObject : IEquatable, IDesignable public CharacterEquip Equip => new((CharacterArmor*)Pointer->EquipSlotData); - public unsafe CharacterWeapon MainHand - => CharacterWeapon.Empty; + public CharacterWeapon MainHand + { + get + { + var child = (byte*)Pointer->CharacterBase.DrawObject.Object.ChildObject; + if (child == null) + return CharacterWeapon.Empty; + + return *(CharacterWeapon*)(child + 0x8F0); + } + } public unsafe CharacterWeapon OffHand - => CharacterWeapon.Empty; + { + get + { + var child = Pointer->CharacterBase.DrawObject.Object.ChildObject; + if (child == null) + return CharacterWeapon.Empty; + + var sibling = (byte*) child->NextSiblingObject; + if (sibling == null) + return CharacterWeapon.Empty; + + return *(CharacterWeapon*)(child + 0x8F0); + } + } public unsafe bool VisorEnabled => (*(byte*)(Address + 0x90) & 0x40) != 0; @@ -113,6 +135,7 @@ public unsafe partial struct Actor : IEquatable, IDesignable ident = GetIdentifier(); return true; } + ident = IIdentifier.Invalid; return false; } @@ -156,16 +179,16 @@ public unsafe partial struct Actor : IEquatable, IDesignable public CharacterEquip Equip => new((CharacterArmor*)Pointer->EquipSlotData); - public unsafe CharacterWeapon MainHand + public CharacterWeapon MainHand { - get => *(CharacterWeapon*)(Address + 0x06C0 + 0x10); - set => *(CharacterWeapon*)(Address + 0x06C0 + 0x10) = value; + get => *(CharacterWeapon*)&Pointer->DrawData.MainHandModel; + set => *(CharacterWeapon*)&Pointer->DrawData.MainHandModel = value; } - public unsafe CharacterWeapon OffHand + public CharacterWeapon OffHand { - get => *(CharacterWeapon*)(Address + 0x06C0 + 0x10 + 0x68); - set => *(CharacterWeapon*)(Address + 0x06C0 + 0x10 + 0x68) = value; + get => *(CharacterWeapon*)&Pointer->DrawData.OffHandModel; + set => *(CharacterWeapon*)&Pointer->DrawData.OffHandModel = value; } public unsafe bool VisorEnabled diff --git a/Glamourer/Interop/RedrawManager.cs b/Glamourer/Interop/RedrawManager.cs index 3722c7a..6e52930 100644 --- a/Glamourer/Interop/RedrawManager.cs +++ b/Glamourer/Interop/RedrawManager.cs @@ -1,13 +1,17 @@ using System; +using System.Reflection.Metadata; +using System.Runtime.InteropServices; using Dalamud.Hooking; using Dalamud.Logging; using Dalamud.Utility.Signatures; +using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using Glamourer.Customization; using Glamourer.State; using Glamourer.Structs; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; +using CustomizeData = Penumbra.GameData.Structs.CustomizeData; using Race = Penumbra.GameData.Enums.Race; namespace Glamourer.Interop; @@ -66,27 +70,30 @@ public unsafe partial class RedrawManager return _flagSlotForUpdateHook.Original(drawObject, slotIdx, data); } - - public bool ChangeEquip(DrawObject drawObject, EquipSlot slot, CharacterArmor data) + public bool ChangeEquip(DrawObject drawObject, uint slotIdx, CharacterArmor data) { if (!drawObject) return false; - var slotIndex = slot.ToIndex(); - if (slotIndex > 9) + if (slotIdx > 9) return false; - return FlagSlotForUpdateDetour(drawObject.Pointer, slotIndex, &data) != 0; + return FlagSlotForUpdateDetour(drawObject.Pointer, slotIdx, &data) != 0; } public bool ChangeEquip(Actor actor, EquipSlot slot, CharacterArmor data) - => actor && ChangeEquip(actor.DrawObject, slot, data); + => actor && ChangeEquip(actor.DrawObject, slot.ToIndex(), data); + + public bool ChangeEquip(DrawObject drawObject, EquipSlot slot, CharacterArmor data) + => ChangeEquip(drawObject, slot.ToIndex(), data); + + public bool ChangeEquip(Actor actor, uint slotIdx, CharacterArmor data) + => actor && ChangeEquip(actor.DrawObject, slotIdx, data); } public unsafe partial class RedrawManager { - // The character weapon object manipulated is inside the actual character. - public const int CharacterWeaponOffset = 0x6C0; + public static readonly int CharacterWeaponOffset = (int) Marshal.OffsetOf("DrawData"); public delegate void LoadWeaponDelegate(IntPtr offsetCharacter, uint slot, ulong weapon, byte redrawOnEquality, byte unk2, byte skipGameObject, @@ -219,7 +226,7 @@ public unsafe partial class RedrawManager : IDisposable var gameObjectCustomize = new Customize((CustomizeData*)actor.Pointer->CustomizeData); if (gameObjectCustomize.Equals(customize)) customize.Load(save.Data.Customize); - + // Compare game object equip data against draw object equip data for transformations. // Apply each piece of equip that should be applied if they correspond. var gameObjectEquip = new CharacterEquip((CharacterArmor*)actor.Pointer->EquipSlotData); @@ -262,14 +269,24 @@ public unsafe partial class RedrawManager : IDisposable [Signature("E8 ?? ?? ?? ?? 41 0F B6 C5 66 41 89 86")] private readonly ChangeCustomizeDelegate _changeCustomize = null!; - public bool UpdateCustomize(DrawObject drawObject, Customize customize) + public bool UpdateCustomize(Actor actor, Customize customize) { - if (!drawObject.Valid) + if (!actor.Valid || !actor.DrawObject.Valid) return false; - - return _changeCustomize(drawObject.Pointer, (byte*)customize.Data, 1); + + var d = actor.DrawObject; + if (NeedsRedraw(d.Customize, customize)) + { + Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw, true); + return true; + } + + return _changeCustomize(d.Pointer, (byte*)customize.Data, 1); } + public static bool NeedsRedraw(Customize lhs, Customize rhs) + => lhs.Race != rhs.Race || lhs.Gender != rhs.Gender || lhs.Face != rhs.Face || lhs.Race == Race.Hyur && lhs.Clan != rhs.Clan; + public static void SetVisor(Human* data, bool on) { diff --git a/Glamourer/State/CharacterSave.cs b/Glamourer/State/CharacterSave.cs index 47f5884..ab90018 100644 --- a/Glamourer/State/CharacterSave.cs +++ b/Glamourer/State/CharacterSave.cs @@ -57,6 +57,53 @@ public enum ApplicationFlags : uint Wet = 0x040000, } +public static class ApplicationFlagExtensions +{ + public static ApplicationFlags ToApplicationFlag(this EquipSlot slot) + => slot switch + { + EquipSlot.MainHand => ApplicationFlags.MainHand, + EquipSlot.OffHand => ApplicationFlags.OffHand, + EquipSlot.Head => ApplicationFlags.Head, + EquipSlot.Body => ApplicationFlags.Body, + EquipSlot.Hands => ApplicationFlags.Hands, + EquipSlot.Legs => ApplicationFlags.Legs, + EquipSlot.Feet => ApplicationFlags.Feet, + EquipSlot.Ears => ApplicationFlags.Ears, + EquipSlot.Neck => ApplicationFlags.Neck, + EquipSlot.Wrists => ApplicationFlags.Wrist, + EquipSlot.RFinger => ApplicationFlags.RFinger, + EquipSlot.BothHand => ApplicationFlags.MainHand | ApplicationFlags.OffHand, + EquipSlot.LFinger => ApplicationFlags.LFinger, + EquipSlot.HeadBody => ApplicationFlags.Body, + EquipSlot.BodyHandsLegsFeet => ApplicationFlags.Body, + EquipSlot.LegsFeet => ApplicationFlags.Legs, + EquipSlot.FullBody => ApplicationFlags.Body, + EquipSlot.BodyHands => ApplicationFlags.Body, + EquipSlot.BodyLegsFeet => ApplicationFlags.Body, + EquipSlot.ChestHands => ApplicationFlags.Body, + _ => 0, + }; + + public static EquipSlot ToSlot(this ApplicationFlags flags) + => flags switch + { + ApplicationFlags.MainHand => EquipSlot.MainHand, + ApplicationFlags.OffHand => EquipSlot.OffHand, + ApplicationFlags.Head => EquipSlot.Head, + ApplicationFlags.Body => EquipSlot.Body, + ApplicationFlags.Hands => EquipSlot.Hands, + ApplicationFlags.Legs => EquipSlot.Legs, + ApplicationFlags.Feet => EquipSlot.Feet, + ApplicationFlags.Ears => EquipSlot.Ears, + ApplicationFlags.Neck => EquipSlot.Neck, + ApplicationFlags.Wrist => EquipSlot.Wrists, + ApplicationFlags.RFinger => EquipSlot.RFinger, + ApplicationFlags.LFinger => EquipSlot.LFinger, + _ => EquipSlot.Unknown, + }; +} + [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct CharacterData { @@ -153,6 +200,8 @@ public struct CharacterData ModelId = designable.ModelId; Customize.Load(designable.Customize); Equipment.Load(designable.Equip); + MainHand = designable.MainHand; + OffHand = designable.OffHand; Flags = SaveFlags | (designable.VisorEnabled ? ApplicationFlags.Visor : 0) | (designable.WeaponEnabled ? ApplicationFlags.Weapon : 0); } } diff --git a/Glamourer/State/CurrentDesign.cs b/Glamourer/State/CurrentDesign.cs index b29b956..d846d83 100644 --- a/Glamourer/State/CurrentDesign.cs +++ b/Glamourer/State/CurrentDesign.cs @@ -28,13 +28,6 @@ public unsafe class CurrentDesign : ICharacterData _drawData = _initialData.Clone(); } - public void SaveCustomization(Customize customize, IReadOnlyCollection actors) - { - _drawData.Customize.Load(customize); - foreach (var actor in actors.Where(a => a && a.DrawObject)) - Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _drawData.Customize); - } - public void Update(Actor actor) { if (!actor)