diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs index f3eabbd..4e0f3de 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Icon.cs @@ -1,4 +1,5 @@ using Glamourer.GameData; +using Glamourer.Unlocks; using ImGuiNET; using OtterGui; using OtterGui.Raii; @@ -70,7 +71,10 @@ public partial class CustomizationDrawer var icon = _service.Manager.GetIcon(custom.IconId); using (var _ = ImRaii.Group()) { - using var frameColor = ImRaii.PushColor(ImGuiCol.Button, Colors.SelectedRed, current == i); + var isFavorite = _favorites.Contains(_set.Gender, _set.Clan, _currentIndex, custom.Value); + using var frameColor = current == i + ? ImRaii.PushColor(ImGuiCol.Button, Colors.SelectedRed) + : ImRaii.PushColor(ImGuiCol.Button, ColorId.FavoriteStarOn.Value(), isFavorite); if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize)) { @@ -78,7 +82,14 @@ public partial class CustomizationDrawer ImGui.CloseCurrentPopup(); } - ImGuiUtil.HoverIconTooltip(icon, _iconSize); + if (ImGui.IsItemClicked(ImGuiMouseButton.Right)) + if (isFavorite) + _favorites.Remove(_set.Gender, _set.Clan, _currentIndex, custom.Value); + else + _favorites.TryAdd(_set.Gender, _set.Clan, _currentIndex, custom.Value); + + ImGuiUtil.HoverIconTooltip(icon, _iconSize, + FavoriteManager.TypeAllowed(_currentIndex) ? "Right-Click to toggle favorite." : string.Empty); var text = custom.Value.ToString(); var textWidth = ImGui.CalcTextSize(text).X; diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.cs b/Glamourer/Gui/Customization/CustomizationDrawer.cs index 4062d69..8cb7f91 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.cs @@ -3,6 +3,7 @@ using Dalamud.Interface.Utility; using Dalamud.Plugin; using Glamourer.GameData; using Glamourer.Services; +using Glamourer.Unlocks; using ImGuiNET; using OtterGui; using OtterGui.Raii; @@ -11,7 +12,7 @@ using Penumbra.GameData.Structs; namespace Glamourer.Gui.Customization; -public partial class CustomizationDrawer(DalamudPluginInterface pi, CustomizeService _service, CodeService _codes, Configuration _config) +public partial class CustomizationDrawer(DalamudPluginInterface pi, CustomizeService _service, CodeService _codes, Configuration _config, FavoriteManager _favorites) : IDisposable { private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f); diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs index ea89e56..aa67fb4 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs @@ -119,6 +119,11 @@ public class UnlockOverview ImGui.Image(icon.ImGuiHandle, iconSize, Vector2.Zero, Vector2.One, unlocked || _codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint); + + if (_favorites.Contains(_selected3, _selected2, customize.Index, customize.Value)) + ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), + 12 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 6 * ImGuiHelpers.GlobalScale); + if (ImGui.IsItemHovered()) { using var tt = ImRaii.Tooltip(); diff --git a/Glamourer/Unlocks/FavoriteManager.cs b/Glamourer/Unlocks/FavoriteManager.cs index 83ff472..f5f80a1 100644 --- a/Glamourer/Unlocks/FavoriteManager.cs +++ b/Glamourer/Unlocks/FavoriteManager.cs @@ -2,16 +2,28 @@ using Glamourer.Services; using Newtonsoft.Json; using OtterGui.Classes; +using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; namespace Glamourer.Unlocks; public class FavoriteManager : ISavable { - private const int CurrentVersion = 1; - private readonly SaveService _saveService; - private readonly HashSet _favorites = new(); - private readonly HashSet _favoriteColors = new(); + private readonly record struct FavoriteHairStyle(Gender Gender, SubRace Race, CustomizeIndex Type, CustomizeValue Id) + { + public uint ToValue() + => (uint)Id.Value | ((uint)Type << 8) | ((uint)Race << 16) | ((uint)Gender << 24); + + public FavoriteHairStyle(uint value) + : this((Gender)((value >> 24) & 0xFF), (SubRace)((value >> 16) & 0xFF), (CustomizeIndex)((value >> 8) & 0xFF), (CustomizeValue)(value & 0xFF)) + { } + } + + private const int CurrentVersion = 1; + private readonly SaveService _saveService; + private readonly HashSet _favorites = []; + private readonly HashSet _favoriteColors = []; + private readonly HashSet _favoriteHairStyles = []; public FavoriteManager(SaveService saveService) { @@ -19,6 +31,14 @@ public class FavoriteManager : ISavable Load(); } + public static bool TypeAllowed(CustomizeIndex type) + => type switch + { + CustomizeIndex.Hairstyle => true, + CustomizeIndex.FacePaint => true, + _ => false, + }; + private void Load() { var file = _saveService.FileNames.FavoriteFile; @@ -40,7 +60,9 @@ public class FavoriteManager : ISavable case 1: _favorites.UnionWith(load.FavoriteItems.Select(i => (ItemId)i)); _favoriteColors.UnionWith(load.FavoriteColors.Select(i => (StainId)i)); + _favoriteHairStyles.UnionWith(load.FavoriteHairStyles.Select(t => new FavoriteHairStyle(t))); break; + default: throw new Exception($"Unknown Version {load.Version}"); } } @@ -53,7 +75,7 @@ public class FavoriteManager : ISavable private void LoadV0(string text) { - var array = JsonConvert.DeserializeObject(text) ?? Array.Empty(); + var array = JsonConvert.DeserializeObject(text) ?? []; _favorites.UnionWith(array.Select(i => (ItemId)i)); Save(); } @@ -66,10 +88,8 @@ public class FavoriteManager : ISavable public void Save(StreamWriter writer) { - using var j = new JsonTextWriter(writer) - { - Formatting = Formatting.Indented, - }; + using var j = new JsonTextWriter(writer); + j.Formatting = Formatting.Indented; j.WriteStartObject(); j.WritePropertyName(nameof(LoadStruct.Version)); j.WriteValue(CurrentVersion); @@ -83,6 +103,11 @@ public class FavoriteManager : ISavable foreach (var stain in _favoriteColors) j.WriteValue(stain.Id); j.WriteEndArray(); + j.WritePropertyName(nameof(LoadStruct.FavoriteHairStyles)); + j.WriteStartArray(); + foreach (var hairStyle in _favoriteHairStyles) + j.WriteValue(hairStyle.ToValue()); + j.WriteEndArray(); j.WriteEndObject(); } @@ -110,6 +135,15 @@ public class FavoriteManager : ISavable return true; } + public bool TryAdd(Gender gender, SubRace race, CustomizeIndex type, CustomizeValue value) + { + if (!TypeAllowed(type) || !_favoriteHairStyles.Add(new FavoriteHairStyle(gender, race, type, value))) + return false; + + Save(); + return true; + } + public bool Remove(EquipItem item) => Remove(item.ItemId); @@ -134,6 +168,15 @@ public class FavoriteManager : ISavable return true; } + public bool Remove(Gender gender, SubRace race, CustomizeIndex type, CustomizeValue value) + { + if (!_favoriteHairStyles.Remove(new FavoriteHairStyle(gender, race, type, value))) + return false; + + Save(); + return true; + } + public bool Contains(EquipItem item) => _favorites.Contains(item.ItemId); @@ -146,11 +189,15 @@ public class FavoriteManager : ISavable public bool Contains(StainId stain) => _favoriteColors.Contains(stain); + public bool Contains(Gender gender, SubRace race, CustomizeIndex type, CustomizeValue value) + => _favoriteHairStyles.Contains(new FavoriteHairStyle(gender, race, type, value)); + private struct LoadStruct { - public int Version = CurrentVersion; - public uint[] FavoriteItems = Array.Empty(); - public byte[] FavoriteColors = Array.Empty(); + public int Version = CurrentVersion; + public uint[] FavoriteItems = []; + public byte[] FavoriteColors = []; + public uint[] FavoriteHairStyles = []; public LoadStruct() { } diff --git a/OtterGui b/OtterGui index 9f9705f..6d6e7b3 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit 9f9705f417114d006c7b1f043637083f0782bb6b +Subproject commit 6d6e7b37c31bc82b8b2811c85a09f67fb0434f8a