diff --git a/Deliveroo/CharacterConfiguration.cs b/Deliveroo/CharacterConfiguration.cs new file mode 100644 index 0000000..d32a59e --- /dev/null +++ b/Deliveroo/CharacterConfiguration.cs @@ -0,0 +1,35 @@ +using System.IO; +using Dalamud.Plugin; +using Newtonsoft.Json; + +namespace Deliveroo; + +internal sealed class CharacterConfiguration +{ + public ulong LocalContentId { get; set; } + public string? CachedPlayerName { get; set; } + public string? CachedWorldName { get; set; } + + public bool DisableForCharacter { get; set; } = false; + public bool UseHideArmouryChestItemsFilter { get; set; } = false; + + public static string ResolveFilePath(DalamudPluginInterface pluginInterface, ulong localContentId) + => Path.Join(pluginInterface.GetPluginConfigDirectory(), $"char.{localContentId:X}.json"); + + public static CharacterConfiguration? Load(DalamudPluginInterface pluginInterface, ulong localContentId) + { + string path = ResolveFilePath(pluginInterface, localContentId); + if (!File.Exists(path)) + return null; + + return JsonConvert.DeserializeObject(File.ReadAllText(path)); + } + + public void Save(DalamudPluginInterface pluginInterface) + { + File.WriteAllText(ResolveFilePath(pluginInterface, LocalContentId), JsonConvert.SerializeObject(this, Formatting.Indented)); + } + + public void Delete(DalamudPluginInterface pluginInterface) => + File.Delete(ResolveFilePath(pluginInterface, LocalContentId)); +} diff --git a/Deliveroo/Configuration.cs b/Deliveroo/Configuration.cs index dfa70f9..8678367 100644 --- a/Deliveroo/Configuration.cs +++ b/Deliveroo/Configuration.cs @@ -11,7 +11,6 @@ internal sealed class Configuration : IPluginConfiguration public List ItemsToPurchase { get; set; } = new(); public int ReservedSealCount { get; set; } = 0; - public ItemFilterType ItemFilter { get; set; } = ItemFilterType.HideGearSetItems; public bool IgnoreCertainLimitations { get; set; } = false; internal sealed class PurchasePriority @@ -20,10 +19,4 @@ internal sealed class Configuration : IPluginConfiguration public int Limit { get; set; } } - public enum ItemFilterType - { - ShowAllItems = 0, - HideGearSetItems = 1, - HideArmouryChestItems = 2, - } } diff --git a/Deliveroo/DeliverooPlugin.Supply.cs b/Deliveroo/DeliverooPlugin.Supply.cs index 88aa4ff..baf3623 100644 --- a/Deliveroo/DeliverooPlugin.Supply.cs +++ b/Deliveroo/DeliverooPlugin.Supply.cs @@ -86,10 +86,11 @@ partial class DeliverooPlugin return; } - if (addonGc->SelectedFilter == 0 || addonGc->SelectedFilter != (int)_configuration.ItemFilter) + ItemFilterType configuredFilter = ResolveSelectedSupplyFilter(); + if (addonGc->SelectedFilter == 0 || addonGc->SelectedFilter != (int)configuredFilter) { _turnInWindow.Error = - $"Wrong filter selected (expected {_configuration.ItemFilter}, but is {(Configuration.ItemFilterType)addonGc->SelectedFilter})"; + $"Wrong filter selected (expected {configuredFilter}, but is {(ItemFilterType)addonGc->SelectedFilter})"; return; } @@ -169,7 +170,7 @@ partial class DeliverooPlugin var updateFilter = stackalloc AtkValue[] { new() { Type = ValueType.Int, Int = 5 }, - new() { Type = ValueType.Int, Int = (int)_configuration.ItemFilter }, + new() { Type = ValueType.Int, Int = (int)ResolveSelectedSupplyFilter() }, new() { Type = 0, Int = 0 } }; addonSupplyList->AtkUnitBase.FireCallback(3, updateFilter); @@ -217,4 +218,12 @@ partial class DeliverooPlugin } } } + + private ItemFilterType ResolveSelectedSupplyFilter() + { + if (CharacterConfiguration is { UseHideArmouryChestItemsFilter: true }) + return ItemFilterType.HideArmouryChestItems; + + return ItemFilterType.HideGearSetItems; + } } diff --git a/Deliveroo/DeliverooPlugin.cs b/Deliveroo/DeliverooPlugin.cs index 63f7145..c901834 100644 --- a/Deliveroo/DeliverooPlugin.cs +++ b/Deliveroo/DeliverooPlugin.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Dalamud.Data; using Dalamud.Game; @@ -18,6 +19,7 @@ using Deliveroo.Windows; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; using Lumina.Excel.GeneratedSheets; +using Newtonsoft.Json; using Condition = Dalamud.Game.ClientState.Conditions.Condition; namespace Deliveroo; @@ -72,7 +74,7 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin _yesAlreadyIpc = new YesAlreadyIpc(dalamudReflector); _configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration(); _gcRewardsCache = new GcRewardsCache(dataManager); - _configWindow = new ConfigWindow(_pluginInterface, this, _configuration, _gcRewardsCache); + _configWindow = new ConfigWindow(_pluginInterface, this, _configuration, _gcRewardsCache, _clientState); _windowSystem.AddWindow(_configWindow); _turnInWindow = new TurnInWindow(this, _pluginInterface, _configuration, _gcRewardsCache, _configWindow); _windowSystem.AddWindow(_turnInWindow); @@ -82,14 +84,22 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin _framework.Update += FrameworkUpdate; _pluginInterface.UiBuilder.Draw += _windowSystem.Draw; _pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle; + _clientState.Login += Login; + _clientState.Logout += Logout; _commandManager.AddHandler("/deliveroo", new CommandInfo(ProcessCommand) { HelpMessage = "Open the configuration" }); + + if (_clientState.IsLoggedIn) + Login(this, EventArgs.Empty); } public string Name => "Deliveroo"; + internal CharacterConfiguration? CharacterConfiguration { get; set; } + + internal Stage CurrentStage { get => _currentStageInternal; @@ -103,13 +113,53 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin } } + private void Login(object? sender, EventArgs e) + { + try + { + CharacterConfiguration = CharacterConfiguration.Load(_pluginInterface, _clientState.LocalContentId); + if (CharacterConfiguration != null) + { + if (CharacterConfiguration.CachedPlayerName != _clientState.LocalPlayer!.Name.ToString() || + CharacterConfiguration.CachedWorldName != + _clientState.LocalPlayer.HomeWorld.GameData!.Name.ToString()) + { + CharacterConfiguration.CachedPlayerName = _clientState.LocalPlayer!.Name.ToString(); + CharacterConfiguration.CachedWorldName = + _clientState.LocalPlayer.HomeWorld.GameData!.Name.ToString(); + + CharacterConfiguration.Save(_pluginInterface); + } + + PluginLog.Information($"Loaded character-specific information for {_clientState.LocalContentId}"); + } + else + { + PluginLog.Verbose( + $"No character-specific information for {_clientState.LocalContentId}"); + } + } + catch (Exception ex) + { + PluginLog.Error(ex, "Unable to load character configuration"); + CharacterConfiguration = null; + } + } + + private void Logout(object? sender, EventArgs e) + { + CharacterConfiguration = null; + } + private unsafe void FrameworkUpdate(Framework f) { _turnInWindow.Error = string.Empty; - if (!_clientState.IsLoggedIn || _clientState.TerritoryType is not 128 and not 130 and not 132 || + if (!_clientState.IsLoggedIn || + _clientState.TerritoryType is not 128 and not 130 and not 132 || _condition[ConditionFlag.OccupiedInCutSceneEvent] || GetDistanceToNpc(GetQuartermasterId(), out GameObject? quartermaster) >= 7f || GetDistanceToNpc(GetPersonnelOfficerId(), out GameObject? personnelOfficer) >= 7f || + CharacterConfiguration is { DisableForCharacter: true } || _configWindow.IsOpen) { _turnInWindow.IsOpen = false; @@ -241,6 +291,8 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin public void Dispose() { _commandManager.RemoveHandler("/deliveroo"); + _clientState.Logout -= Logout; + _clientState.Login -= Login; _pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle; _pluginInterface.UiBuilder.Draw -= _windowSystem.Draw; _framework.Update -= FrameworkUpdate; diff --git a/Deliveroo/GameData/ItemFilterType.cs b/Deliveroo/GameData/ItemFilterType.cs new file mode 100644 index 0000000..7e9b4f4 --- /dev/null +++ b/Deliveroo/GameData/ItemFilterType.cs @@ -0,0 +1,8 @@ +namespace Deliveroo.GameData; + +public enum ItemFilterType +{ + ShowAllItems = 0, + HideGearSetItems = 1, + HideArmouryChestItems = 2, +} diff --git a/Deliveroo/Windows/ConfigWindow.cs b/Deliveroo/Windows/ConfigWindow.cs index d86bdd2..53ceaec 100644 --- a/Deliveroo/Windows/ConfigWindow.cs +++ b/Deliveroo/Windows/ConfigWindow.cs @@ -2,8 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using Dalamud.Game.ClientState; using Dalamud.Interface; +using Dalamud.Interface.Components; using Dalamud.Interface.Windowing; +using Dalamud.Logging; using Dalamud.Plugin; using Deliveroo.GameData; using FFXIVClientStructs.FFXIV.Client.UI.Agent; @@ -13,24 +16,24 @@ namespace Deliveroo.Windows; internal sealed class ConfigWindow : Window { - private static string[] _itemFilterValues = { "Hide Gear Set Items", "Hide Armoury Chest Items" }; - private readonly DalamudPluginInterface _pluginInterface; private readonly DeliverooPlugin _plugin; private readonly Configuration _configuration; private readonly GcRewardsCache _gcRewardsCache; + private readonly ClientState _clientState; private readonly Dictionary _itemLookup; private uint _dragDropSource = 0; public ConfigWindow(DalamudPluginInterface pluginInterface, DeliverooPlugin plugin, Configuration configuration, - GcRewardsCache gcRewardsCache) + GcRewardsCache gcRewardsCache, ClientState clientState) : base("Deliveroo - Configuration###DeliverooConfig") { _pluginInterface = pluginInterface; _plugin = plugin; _configuration = configuration; _gcRewardsCache = gcRewardsCache; + _clientState = clientState; _itemLookup = _gcRewardsCache.Rewards.Values .SelectMany(x => x) @@ -58,6 +61,7 @@ internal sealed class ConfigWindow : Window if (ImGui.BeginTabBar("DeliverooConfigTabs")) { DrawBuyList(); + DrawCharacterSpecificSettings(); DrawAdditionalSettings(); ImGui.EndTabBar(); @@ -154,7 +158,86 @@ internal sealed class ConfigWindow : Window else { int currentItem = 0; - ImGui.Combo("Add Item", ref currentItem, new string[] { "(Not part of a GC)" }, 1); + ImGui.Combo("Add Item", ref currentItem, new[] { "(Not part of a GC)" }, 1); + } + + ImGui.EndTabItem(); + } + } + + private void DrawCharacterSpecificSettings() + { + if (ImGui.BeginTabItem("Character Settings")) + { + if (_clientState is { IsLoggedIn: true, LocalContentId: > 0 }) + { + string currentCharacterName = _clientState.LocalPlayer!.Name.ToString(); + string currentWorldName = _clientState.LocalPlayer.HomeWorld.GameData!.Name.ToString(); + ImGui.Text($"Current Character: {currentCharacterName} @ {currentWorldName}"); + ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + + var charConfiguration = _plugin.CharacterConfiguration; + if (charConfiguration != null) + { + + bool disableForCharacter = charConfiguration.DisableForCharacter; + if (ImGui.Checkbox("Disable plugin for this character", ref disableForCharacter)) + { + charConfiguration.DisableForCharacter = disableForCharacter; + charConfiguration.Save(_pluginInterface); + } + + ImGui.BeginDisabled(charConfiguration.DisableForCharacter); + bool useHideArmouryChestItemsFilter = charConfiguration.UseHideArmouryChestItemsFilter; + if (ImGui.Checkbox("Use 'Hide Armoury Chest Items' filter", ref useHideArmouryChestItemsFilter)) + { + charConfiguration.UseHideArmouryChestItemsFilter = useHideArmouryChestItemsFilter; + charConfiguration.Save(_pluginInterface); + } + if (ImGui.IsItemHovered()) + ImGui.SetTooltip("The default filter for all characters is 'Hide Gear Set Items', but you may want to override this to hide all Armoury Chest items (regardless of whether they're part of a gear set) e.g. for your main character."); + + ImGui.EndDisabled(); + ImGui.Spacing(); + ImGui.Separator(); + ImGui.Spacing(); + + ImGui.BeginDisabled(!ImGui.GetIO().KeyCtrl); + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.PersonCircleMinus, + "Remove character-specific settings")) + { + charConfiguration.Delete(_pluginInterface); + _plugin.CharacterConfiguration = null; + } + + ImGui.EndDisabled(); + if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled) && !ImGui.GetIO().KeyCtrl) + ImGui.SetTooltip( + $"Hold CTRL to remove the configuration for {currentCharacterName} (non-reversible)."); + } + else + { + // no settings + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.PersonCirclePlus, + "Enable character-specific settings")) + { + _plugin.CharacterConfiguration = new() + { + LocalContentId = _clientState.LocalContentId, + CachedPlayerName = currentCharacterName, + CachedWorldName = currentWorldName, + }; + _plugin.CharacterConfiguration.Save(_pluginInterface); + PluginLog.Information( + $"Created character-specific configuration for {_clientState.LocalContentId}"); + } + } + } + else + { + ImGui.Text("You are not currently logged in."); } ImGui.EndTabItem(); @@ -173,14 +256,6 @@ internal sealed class ConfigWindow : Window Save(); } - ImGui.Separator(); - int selectedItemFilter = Math.Max(0, (int)_configuration.ItemFilter - 1); - if (ImGui.Combo("Item Filter", ref selectedItemFilter, _itemFilterValues, _itemFilterValues.Length)) - { - _configuration.ItemFilter = (Configuration.ItemFilterType)(selectedItemFilter + 1); - Save(); - } - ImGui.EndTabItem(); } }