From 189d4fb0a78fc00773e19af6abb52f7c8ac3c6b2 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Mon, 9 Oct 2023 01:48:27 +0200 Subject: [PATCH] (2.x) Update Retainer/Group config tabs --- ARControl/ARControl.csproj | 4 +- ARControl/AutoRetainerControlPlugin.Sync.cs | 2 +- ARControl/AutoRetainerControlPlugin.cs | 29 +- ARControl/Configuration.cs | 51 ++- ARControl/Windows/ConfigWindow.cs | 325 ++++++++++++++++++-- 5 files changed, 377 insertions(+), 34 deletions(-) diff --git a/ARControl/ARControl.csproj b/ARControl/ARControl.csproj index 7256420..a5a4607 100644 --- a/ARControl/ARControl.csproj +++ b/ARControl/ARControl.csproj @@ -1,7 +1,7 @@ net7.0-windows - 1.0 + 2.0 11.0 enable true @@ -16,7 +16,7 @@ $(appdata)\XIVLauncher\addon\Hooks\dev\ - $(appdata)\XIVLauncher\installedPlugins\AutoRetainer\4.2.0.6\ + $(appdata)\XIVLauncher\installedPlugins\AutoRetainer\4.2.1.0\ diff --git a/ARControl/AutoRetainerControlPlugin.Sync.cs b/ARControl/AutoRetainerControlPlugin.Sync.cs index 45a5fa4..3f1c16b 100644 --- a/ARControl/AutoRetainerControlPlugin.Sync.cs +++ b/ARControl/AutoRetainerControlPlugin.Sync.cs @@ -24,7 +24,7 @@ partial class AutoRetainerControlPlugin LocalContentId = registeredCharacterId, CharacterName = offlineCharacterData.Name, WorldName = offlineCharacterData.World, - Managed = false, + Type = Configuration.CharacterType.NotManaged, }; save = true; diff --git a/ARControl/AutoRetainerControlPlugin.cs b/ARControl/AutoRetainerControlPlugin.cs index e425186..dac1bd0 100644 --- a/ARControl/AutoRetainerControlPlugin.cs +++ b/ARControl/AutoRetainerControlPlugin.cs @@ -6,7 +6,6 @@ using ARControl.Windows; using AutoRetainerAPI; using Dalamud.Game.Command; using Dalamud.Interface; -using Dalamud.Interface.Components; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; @@ -41,11 +40,13 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin _commandManager = commandManager; _pluginLog = pluginLog; - _configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration(); + _configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration { Version = 2 }; _gameCache = new GameCache(dataManager); _ventureResolver = new VentureResolver(_gameCache, _pluginLog); - _configWindow = new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _pluginLog); + _configWindow = + new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _pluginLog) + { IsOpen = true }; _windowSystem.AddWindow(_configWindow); ECommonsMain.Init(_pluginInterface, this); @@ -72,7 +73,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin { _pluginLog.Information("No character information found"); } - else if (!ch.Managed) + else if (ch.Type == Configuration.CharacterType.NotManaged) { _pluginLog.Information("Character is not managed"); } @@ -91,6 +92,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin { _pluginLog.Information("Checking tasks..."); Sync(); + /* FIXME foreach (var queuedItem in _configuration.QueuedItems.Where(x => x.RemainingQuantity > 0)) { _pluginLog.Information($"Checking venture info for itemId {queuedItem.ItemId}"); @@ -115,6 +117,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin return; } } + */ // fallback: managed but no venture found if (retainer.LastVenture != 395) @@ -136,7 +139,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin { Configuration.CharacterConfiguration? characterConfiguration = _configuration.Characters.FirstOrDefault(x => x.LocalContentId == characterId); - if (characterConfiguration is not { Managed: true }) + if (characterConfiguration is not { Type: not Configuration.CharacterType.NotManaged }) return; Configuration.RetainerConfiguration? retainer = @@ -145,7 +148,21 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin return; ImGui.SameLine(); - ImGuiComponents.IconButton(FontAwesomeIcon.Book); + ImGui.PushFont(UiBuilder.IconFont); + ImGui.Text(FontAwesomeIcon.Book.ToIconString()); + ImGui.PopFont(); + if (ImGui.IsItemHovered()) + { + string text = "This retainer is managed by ARC."; + + if (characterConfiguration.Type == Configuration.CharacterType.PartOfCharacterGroup) + { + var group = _configuration.CharacterGroups.Single(x => x.Id == characterConfiguration.CharacterGroupId); + text += $"\n\nCharacter Group: {group.Name}"; + } + + ImGui.SetTooltip(text); + } } private void TerritoryChanged(ushort e) => Sync(); diff --git a/ARControl/Configuration.cs b/ARControl/Configuration.cs index 06eae06..e7ec011 100644 --- a/ARControl/Configuration.cs +++ b/ARControl/Configuration.cs @@ -1,14 +1,31 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Dalamud.Configuration; +using Dalamud.Interface; namespace ARControl; internal sealed class Configuration : IPluginConfiguration { - public int Version { get; set; } = 1; + public int Version { get; set; } - public List QueuedItems { get; set; } = new(); public List Characters { get; set; } = new(); + public List ItemLists { get; set; } = new(); + public List CharacterGroups { get; set; } = new(); + + public sealed class ItemList + { + public required Guid Id { get; set; } + public required string Name { get; set; } + public required ListType Type { get; set; } = ListType.CollectOneTime; + public List Items { get; set; } = new(); + } + + public enum ListType + { + CollectOneTime, + KeepAlways, + } public sealed class QueuedItem { @@ -16,12 +33,23 @@ internal sealed class Configuration : IPluginConfiguration public required int RemainingQuantity { get; set; } } + public class CharacterGroup + { + public required Guid Id { get; set; } + public required string Name { get; set; } + public required FontAwesomeIcon Icon { get; set; } + public List ItemListIds { get; set; } = new(); + } + public sealed class CharacterConfiguration { public required ulong LocalContentId { get; set; } public required string CharacterName { get; set; } public required string WorldName { get; set; } - public required bool Managed { get; set; } + + public CharacterType Type { get; set; } = CharacterType.NotManaged; + public Guid CharacterGroupId { get; set; } + public List ItemListIds { get; set; } = new(); public List Retainers { get; set; } = new(); public HashSet GatheredItems { get; set; } = new(); @@ -29,6 +57,21 @@ internal sealed class Configuration : IPluginConfiguration public override string ToString() => $"{CharacterName} @ {WorldName}"; } + public enum CharacterType + { + NotManaged, + + /// + /// The character's item list(s) are manually selected. + /// + Standalone, + + /// + /// All item lists are managed through the character group. + /// + PartOfCharacterGroup + } + public sealed class RetainerConfiguration { public required string Name { get; set; } diff --git a/ARControl/Windows/ConfigWindow.cs b/ARControl/Windows/ConfigWindow.cs index c1cb8be..82a1b4a 100644 --- a/ARControl/Windows/ConfigWindow.cs +++ b/ARControl/Windows/ConfigWindow.cs @@ -1,13 +1,16 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Numerics; using ARControl.GameData; +using Dalamud.Game.Text; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; +using ECommons; using ECommons.ImGuiMethods; using ImGuiNET; @@ -31,6 +34,7 @@ internal sealed class ConfigWindow : Window private string _searchString = string.Empty; private Configuration.QueuedItem? _dragDropSource; private bool _enableDragDrop; + private string _newGroupName = string.Empty; private bool _checkPerCharacter = true; private bool _onlyShowMissing = true; @@ -49,22 +53,30 @@ internal sealed class ConfigWindow : Window _clientState = clientState; _commandManager = commandManager; _pluginLog = pluginLog; + + SizeConstraints = new() + { + MinimumSize = new Vector2(480, 300), + MaximumSize = new Vector2(9999, 9999), + }; } public override void Draw() { if (ImGui.BeginTabBar("ARConfigTabs")) { - DrawItemQueue(); + //DrawItemQueue(); DrawCharacters(); - DrawGatheredItemsToCheck(); + DrawCharacterGroups(); + //DrawGatheredItemsToCheck(); ImGui.EndTabBar(); } } + /* private unsafe void DrawItemQueue() { - if (ImGui.BeginTabItem("Venture Queue")) + if (ImGui.BeginTabItem("Item Lists")) { if (ImGui.BeginCombo("Add Item...##VentureSelection", "")) { @@ -192,7 +204,7 @@ internal sealed class ConfigWindow : Window ImGui.EndTabItem(); } } - +*/ private void DrawCharacters() { if (ImGui.BeginTabItem("Retainers")) @@ -208,9 +220,9 @@ internal sealed class ConfigWindow : Window { ImGui.PushID($"Char{character.LocalContentId}"); - ImGui.PushItemWidth(ImGui.GetFontSize() * 30); + ImGui.SetNextItemWidth(ImGui.GetFontSize() * 30); Vector4 buttonColor = new Vector4(); - if (character is { Managed: true, Retainers.Count: > 0 }) + if (character is { Type: not Configuration.CharacterType.NotManaged, Retainers.Count: > 0 }) { if (character.Retainers.All(x => x.Managed)) buttonColor = ImGuiColors.HealerGreen; @@ -222,33 +234,111 @@ internal sealed class ConfigWindow : Window if (ImGuiComponents.IconButton(FontAwesomeIcon.Book, buttonColor)) { - character.Managed = !character.Managed; + if (character.Type == Configuration.CharacterType.NotManaged) + { + character.Type = Configuration.CharacterType.Standalone; + character.CharacterGroupId = Guid.Empty; + } + else + { + character.Type = Configuration.CharacterType.NotManaged; + character.CharacterGroupId = Guid.Empty; + } + Save(); } ImGui.SameLine(); if (ImGui.CollapsingHeader( - $"{character.CharacterName} {(character.Managed ? $"({character.Retainers.Count(x => x.Managed)} / {character.Retainers.Count})" : "")}###{character.LocalContentId}")) + $"{character.CharacterName} {(character.Type != Configuration.CharacterType.NotManaged ? $"({character.Retainers.Count(x => x.Managed)} / {character.Retainers.Count})" : "")}###{character.LocalContentId}")) { ImGui.Indent(30); - foreach (var retainer in character.Retainers.Where(x => x.Job > 0).OrderBy(x => x.DisplayOrder)) + + List<(Guid Id, string Name)> groups = + new List<(Guid Id, string Name)> { (Guid.Empty, "No Group (manually assign lists)") } + .Concat(_configuration.CharacterGroups.Select(x => (x.Id, x.Name))) + .ToList(); + + + if (ImGui.BeginTabBar("CharOptions")) { - ImGui.BeginDisabled(retainer.Level < MaxLevel); - - bool managed = retainer.Managed && retainer.Level == MaxLevel; - ImGui.Text(_gameCache.Jobs[retainer.Job]); - ImGui.SameLine(); - if (ImGui.Checkbox($"{retainer.Name}###Retainer{retainer.Name}{retainer.DisplayOrder}", - ref managed)) + if (character.Type != Configuration.CharacterType.NotManaged && ImGui.BeginTabItem("Venture Lists")) { - retainer.Managed = managed; - Save(); - } + int groupIndex = 0; + if (character.Type == Configuration.CharacterType.PartOfCharacterGroup) + groupIndex = groups.FindIndex(x => x.Id == character.CharacterGroupId); + if (ImGui.Combo("Character Group", ref groupIndex, groups.Select(x => x.Name).ToArray(), + groups.Count)) + { + if (groupIndex == 0) + { + character.Type = Configuration.CharacterType.Standalone; + character.CharacterGroupId = Guid.Empty; + } + else + { + character.Type = Configuration.CharacterType.PartOfCharacterGroup; + character.CharacterGroupId = groups[groupIndex].Id; + } + Save(); + } - ImGui.EndDisabled(); + ImGui.Separator(); + if (groupIndex == 0) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (character.ItemListIds == null) + character.ItemListIds = new(); + DrawListSelection(character.LocalContentId.ToString(), character.ItemListIds); + } + else + { + ImGui.TextWrapped($"Retainers will participate in the following lists:"); + ImGui.Indent(30); + + var group = _configuration.CharacterGroups.Single(x => x.Id == groups[groupIndex].Id); + var lists = group.ItemListIds + .Where(listId => listId != Guid.Empty) + .Select(listId => _configuration.ItemLists.SingleOrDefault(x => x.Id == listId)) + .ToList(); + if (lists.Count > 0) + { + foreach (var list in lists) + ImGui.TextUnformatted($"{SeIconChar.LinkMarker.ToIconChar()} {list.Name}"); + } + else + ImGui.TextColored(ImGuiColors.DalamudRed, "(None)"); + + ImGui.Unindent(30); + ImGui.Spacing(); + } + ImGui.EndTabItem(); + } + if (ImGui.BeginTabItem("Retainers")) + { + foreach (var retainer in character.Retainers.Where(x => x.Job > 0).OrderBy(x => x.DisplayOrder)) + { + ImGui.BeginDisabled(retainer.Level < MaxLevel); + + bool managed = retainer.Managed && retainer.Level == MaxLevel; + ImGui.Text(_gameCache.Jobs[retainer.Job]); + ImGui.SameLine(); + if (ImGui.Checkbox($"{retainer.Name}###Retainer{retainer.Name}{retainer.DisplayOrder}", + ref managed)) + { + retainer.Managed = managed; + Save(); + } + + ImGui.EndDisabled(); + } + ImGui.EndTabItem(); + } + ImGui.EndTabBar(); } + ImGui.Unindent(30); } @@ -260,6 +350,91 @@ internal sealed class ConfigWindow : Window } } + private void DrawCharacterGroups() + { + if (ImGui.BeginTabItem("Groups")) + { + foreach (var group in _configuration.CharacterGroups) + { + ImGui.PushID($"##Group{group.Id}"); + + ImGuiComponents.IconButton(FontAwesomeIcon.Cog); + ImGui.SameLine(); + + var assignedCharacters = _configuration.Characters + .Where(x => x.Type == Configuration.CharacterType.PartOfCharacterGroup && + x.CharacterGroupId == group.Id) + .ToList(); + string countLabel = assignedCharacters.Count == 0 ? "no characters" + : assignedCharacters.Count == 1 ? "1 character" + : $"{assignedCharacters.Count} characters"; + if (ImGui.CollapsingHeader($"{group.Name} ({countLabel})")) + { + ImGui.Indent(30); + if (ImGui.BeginTabBar("GroupOptions")) + { + if (ImGui.BeginTabItem("Venture Lists")) + { + DrawListSelection(group.Id.ToString(), group.ItemListIds); + ImGui.EndTabItem(); + } + + if (ImGui.BeginTabItem("Characters")) + { + ImGui.Text("Characters in this group:"); + ImGui.Indent(30); + foreach (var character in assignedCharacters.OrderBy(x => x.WorldName) + .ThenBy(x => x.LocalContentId)) + ImGui.TextUnformatted($"{character.CharacterName} @ {character.WorldName}"); + ImGui.Unindent(30); + } + + ImGui.EndTabBar(); + } + + ImGui.Unindent(30); + } + + ImGui.PopID(); + } + + ImGui.Separator(); + + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Group")) + ImGui.OpenPopup("##AddGroup"); + + if (ImGui.BeginPopup("##AddGroup")) + { + bool save = ImGui.InputTextWithHint("", "Group Name...", ref _newGroupName, 64, ImGuiInputTextFlags.EnterReturnsTrue); + bool canSave = _newGroupName.Length >= 2 && + !_configuration.CharacterGroups.Any(x => _newGroupName.EqualsIgnoreCase(x.Name)); + ImGui.BeginDisabled(!canSave); + save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save"); + ImGui.EndDisabled(); + + if (canSave && save) + { + _configuration.CharacterGroups.Add(new Configuration.CharacterGroup + { + Id = Guid.NewGuid(), + Name = _newGroupName, + Icon = FontAwesomeIcon.None, + ItemListIds = new(), + }); + + _newGroupName = string.Empty; + + ImGui.CloseCurrentPopup(); + Save(); + } + ImGui.EndPopup(); + } + + ImGui.EndTabItem(); + } + } + + /* private void DrawGatheredItemsToCheck() { if (ImGui.BeginTabItem("Locked Items")) @@ -377,6 +552,114 @@ internal sealed class ConfigWindow : Window ImGui.EndTabItem(); } + }*/ + + private void DrawListSelection(string id, List selectedLists) + { + ImGui.PushID($"##ListSelection{id}"); + + List<(Guid Id, string Name, Configuration.ItemList List)> itemLists = new List + { + new Configuration.ItemList + { + Id = Guid.Empty, + Name = "---", + Type = Configuration.ListType.CollectOneTime, + } + }.Concat(_configuration.ItemLists) + .Select(x => (x.Id, x.Name, x)).ToList(); + int? itemToRemove = null; + for (int i = 0; i < selectedLists.Count; ++i) + { + + ImGui.PushID($"##{id}_Item{i}"); + var listId = selectedLists[i]; + var listIndex = itemLists.FindIndex(x => x.Id == listId); + + if (ImGui.Combo("", ref listIndex, itemLists.Select(x => x.Name).ToArray(), itemLists.Count)) + { + selectedLists[i] = itemLists[listIndex].Id; + Save(); + } + + ImGui.SameLine(); + if (ImGuiComponents.IconButton($"##Jump{i}", FontAwesomeIcon.Edit)) + { + + } + + ImGui.SameLine(); + if (ImGuiComponents.IconButton($"##Up{i}", FontAwesomeIcon.ArrowUp)) + { + + } + + ImGui.SameLine(0, 0); + if (ImGuiComponents.IconButton($"##Down{i}", FontAwesomeIcon.ArrowDown)) + { + + } + + ImGui.SameLine(); + if (ImGuiComponents.IconButton($"##Remove{i}", FontAwesomeIcon.Times)) + itemToRemove = i; + + if (listIndex > 0) + { + if (selectedLists.Take(i).Any(x => x == listId)) + { + ImGui.Indent(30); + ImGui.TextColored(ImGuiColors.DalamudYellow, "This entry is a duplicate and will be ignored."); + ImGui.Unindent(30); + } + else + { + var list = itemLists[listIndex].List; + ImGui.Indent(30); + ImGui.Text(list.Type == Configuration.ListType.CollectOneTime + ? $"{SeIconChar.LinkMarker.ToIconString()} Items on this list will be collected once." + : $"{SeIconChar.LinkMarker.ToIconString()} Items on this list will be kept in stock on each character."); + ImGui.Spacing(); + foreach (var item in list.Items) + { + var venture = _gameCache.Ventures.First(x => x.ItemId == item.ItemId); + ImGui.Text($"{item.RemainingQuantity}x {venture.Name}"); + } + + ImGui.Unindent(30); + } + } + + ImGui.PopID(); + } + + if (itemToRemove != null) + { + selectedLists.RemoveAt(itemToRemove.Value); + Save(); + } + + var unusedLists = itemLists.Where(x => x.Id != Guid.Empty && !selectedLists.Contains(x.Id)).ToList(); + ImGui.BeginDisabled(unusedLists.Count == 0); + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Venture List to this Group")) + ImGui.OpenPopup($"##AddItem{id}"); + + if (ImGui.BeginPopupContextItem($"##AddItem{id}")) + { + foreach (var list in unusedLists) + { + if (ImGui.MenuItem($"{list.Name}##{list.Id}")) + { + selectedLists.Add(list.Id); + Save(); + } + } + + ImGui.EndPopup(); + } + ImGui.EndDisabled(); + + ImGui.PopID(); } private void Save()