From 189d4fb0a78fc00773e19af6abb52f7c8ac3c6b2 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Mon, 9 Oct 2023 01:48:27 +0200 Subject: [PATCH 1/8] (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() From 6564a49369946e2ae42499aef2cae0726f6beed8 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Tue, 10 Oct 2023 12:08:26 +0200 Subject: [PATCH 2/8] (2.x) Update Lists/locked items tabs --- ARControl/Configuration.cs | 11 +- ARControl/Windows/ConfigWindow.cs | 694 +++++++++++++++++++++--------- 2 files changed, 503 insertions(+), 202 deletions(-) diff --git a/ARControl/Configuration.cs b/ARControl/Configuration.cs index e7ec011..77bdfdf 100644 --- a/ARControl/Configuration.cs +++ b/ARControl/Configuration.cs @@ -18,13 +18,20 @@ internal sealed class Configuration : IPluginConfiguration public required Guid Id { get; set; } public required string Name { get; set; } public required ListType Type { get; set; } = ListType.CollectOneTime; + public required ListPriority Priority { get; set; } = ListPriority.InOrder; public List Items { get; set; } = new(); } public enum ListType { CollectOneTime, - KeepAlways, + KeepStocked, + } + + public enum ListPriority + { + InOrder, + Balanced, } public sealed class QueuedItem @@ -33,7 +40,7 @@ internal sealed class Configuration : IPluginConfiguration public required int RemainingQuantity { get; set; } } - public class CharacterGroup + public sealed class CharacterGroup { public required Guid Id { get; set; } public required string Name { get; set; } diff --git a/ARControl/Windows/ConfigWindow.cs b/ARControl/Windows/ConfigWindow.cs index 82a1b4a..52da773 100644 --- a/ARControl/Windows/ConfigWindow.cs +++ b/ARControl/Windows/ConfigWindow.cs @@ -23,6 +23,8 @@ internal sealed class ConfigWindow : Window private static readonly Vector4 ColorGreen = ImGuiColors.HealerGreen; private static readonly Vector4 ColorRed = ImGuiColors.DalamudRed; private static readonly Vector4 ColorGrey = ImGuiColors.DalamudGrey; + private static readonly string[] StockingTypeLabels = { "Collect Once", "Keep in Stock" }; + private static readonly string[] PriorityLabels = { "Collect in order of the list", "Collect item with lowest inventory first" }; private readonly DalamudPluginInterface _pluginInterface; private readonly Configuration _configuration; @@ -31,10 +33,15 @@ internal sealed class ConfigWindow : Window private readonly ICommandManager _commandManager; private readonly IPluginLog _pluginLog; + private readonly Dictionary _currentEditPopups = new(); private string _searchString = string.Empty; - private Configuration.QueuedItem? _dragDropSource; - private bool _enableDragDrop; - private string _newGroupName = string.Empty; + private TemporaryConfig _newGroup = new() { Name = string.Empty }; + private TemporaryConfig _newList = new() + { + Name = string.Empty, + ListType = Configuration.ListType.CollectOneTime, + ListPriority = Configuration.ListPriority.InOrder + }; private bool _checkPerCharacter = true; private bool _onlyShowMissing = true; @@ -65,146 +72,294 @@ internal sealed class ConfigWindow : Window { if (ImGui.BeginTabBar("ARConfigTabs")) { - //DrawItemQueue(); - DrawCharacters(); + DrawVentureLists(); DrawCharacterGroups(); - //DrawGatheredItemsToCheck(); + DrawCharacters(); + DrawGatheredItemsToCheck(); ImGui.EndTabBar(); } } - /* - private unsafe void DrawItemQueue() + private unsafe void DrawVentureLists() { - if (ImGui.BeginTabItem("Item Lists")) + if (ImGui.BeginTabItem("Venture Lists")) { - if (ImGui.BeginCombo("Add Item...##VentureSelection", "")) + Configuration.ItemList? listToDelete = null; + foreach (var list in _configuration.ItemLists) { - ImGuiEx.SetNextItemFullWidth(); - ImGui.InputTextWithHint("", "Filter...", ref _searchString, 256); + ImGui.PushID($"List{list.Id}"); - foreach (var ventures in _gameCache.Ventures - .Where(x => x.Name.ToLower().Contains(_searchString.ToLower())) - .OrderBy(x => x.Level) - .ThenBy(x => x.Name) - .ThenBy(x => x.ItemId) - .GroupBy(x => x.ItemId)) + if (ImGuiComponents.IconButton(FontAwesomeIcon.Cog)) { - var venture = ventures.First(); - if (ImGui.Selectable( - $"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})##SelectVenture{venture.RowId}")) + _currentEditPopups[list.Id] = new TemporaryConfig { - _configuration.QueuedItems.Add(new Configuration.QueuedItem - { - ItemId = venture.ItemId, - RemainingQuantity = 0, - }); - _searchString = string.Empty; - Save(); - } + Name = list.Name, + ListType = list.Type, + ListPriority = list.Priority, + }; + ImGui.OpenPopup($"##EditList{list.Id}"); } + DrawVentureListEditorPopup(list, ref listToDelete); - ImGui.EndCombo(); - } - - ImGui.Separator(); - - ImGui.Indent(30); - - Configuration.QueuedItem? itemToRemove = null; - Configuration.QueuedItem? itemToAdd = null; - int indexToAdd = 0; - for (int i = 0; i < _configuration.QueuedItems.Count; ++i) - { - var item = _configuration.QueuedItems[i]; - ImGui.PushID($"QueueItem{i}"); - var ventures = _gameCache.Ventures.Where(x => x.ItemId == item.ItemId).ToList(); - var venture = ventures.First(); - - if (!_enableDragDrop) + ImGui.SameLine(); + if (ImGui.CollapsingHeader($"{list.Name} {(list.Type == Configuration.ListType.CollectOneTime ? SeIconChar.BoxedNumber1.ToIconChar() : SeIconChar.Circle.ToIconChar())}")) { - ImGui.SetNextItemWidth(130); - int quantity = item.RemainingQuantity; - if (ImGui.InputInt($"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})", - ref quantity, 100)) - { - item.RemainingQuantity = quantity; - Save(); - } + ImGui.Indent(30); + DrawVentureListItemSelection(list); + ImGui.Unindent(30); } - else - { - ImGui.Selectable($"{item.RemainingQuantity}x {venture.Name}"); - - if (ImGui.BeginDragDropSource()) - { - ImGui.SetDragDropPayload("ArcDragDrop", nint.Zero, 0); - _dragDropSource = item; - - ImGui.EndDragDropSource(); - } - - if (ImGui.BeginDragDropTarget()) - { - if (_dragDropSource != null && ImGui.AcceptDragDropPayload("ArcDragDrop").NativePtr != null) - { - itemToAdd = _dragDropSource; - indexToAdd = i; - - _dragDropSource = null; - } - - ImGui.EndDragDropTarget(); - } - } - - ImGui.OpenPopupOnItemClick($"###ctx{i}", ImGuiPopupFlags.MouseButtonRight); - if (ImGui.BeginPopupContextItem($"###ctx{i}")) - { - if (ImGui.MenuItem($"Remove {venture.Name}")) - itemToRemove = item; - - ImGui.EndPopup(); - } - ImGui.PopID(); } - if (itemToRemove != null) + if (listToDelete != null) { - _configuration.QueuedItems.Remove(itemToRemove); + _configuration.ItemLists.Remove(listToDelete); Save(); } - if (itemToAdd != null) - { - _pluginLog.Information($"Updating {itemToAdd.ItemId} → {indexToAdd}"); - _configuration.QueuedItems.Remove(itemToAdd); - _configuration.QueuedItems.Insert(indexToAdd, itemToAdd); - Save(); - } - - ImGui.Unindent(30); - - if (_configuration.QueuedItems.Count > 0) - ImGui.Separator(); - - if (ImGuiComponents.IconButtonWithText(_enableDragDrop ? FontAwesomeIcon.Times : FontAwesomeIcon.Sort, _enableDragDrop ? "Disable Drag&Drop" : "Enable Drag&Drop")) - { - _enableDragDrop = !_enableDragDrop; - } - - ImGui.SameLine(); - if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Remove all finished items")) - { - if (_configuration.QueuedItems.RemoveAll(q => q.RemainingQuantity == 0) > 0) - Save(); - } - + ImGui.Separator(); + DrawNewVentureList(); ImGui.EndTabItem(); } } -*/ + + private void DrawVentureListEditorPopup(Configuration.ItemList list, ref Configuration.ItemList? listToDelete) + { + var assignedCharacters = _configuration.Characters + .Where(x => x.Type == Configuration.CharacterType.Standalone && x.ItemListIds.Contains(list.Id)) + .OrderBy(x => x.WorldName) + .ThenBy(x => x.LocalContentId) + .ToList(); + var assignedGroups = _configuration.CharacterGroups + .Where(x => x.ItemListIds.Contains(list.Id)) + .ToList(); + if (_currentEditPopups.TryGetValue(list.Id, out TemporaryConfig? temporaryConfig) && + ImGui.BeginPopup($"##EditList{list.Id}")) + { + var (save, canSave) = DrawVentureListEditor(temporaryConfig, list); + ImGui.BeginDisabled(!canSave || (list.Name == temporaryConfig.Name && + list.Type == temporaryConfig.ListType && + list.Priority == temporaryConfig.ListPriority)); + save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save"); + ImGui.EndDisabled(); + + if (save && canSave) + { + list.Name = temporaryConfig.Name; + list.Type = temporaryConfig.ListType; + + if (list.Type == Configuration.ListType.CollectOneTime) + list.Priority = Configuration.ListPriority.InOrder; + else + list.Priority = temporaryConfig.ListPriority; + + ImGui.CloseCurrentPopup(); + Save(); + } + else + { + ImGui.SameLine(); + ImGui.BeginDisabled(assignedCharacters.Count > 0 || assignedGroups.Count > 0); + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, "Delete")) + { + listToDelete = list; + ImGui.CloseCurrentPopup(); + } + + ImGui.EndDisabled(); + if ((assignedCharacters.Count > 0 || assignedGroups.Count > 0) && + ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) + { + ImGui.BeginTooltip(); + ImGui.Text( + $"Remove this list from the {assignedCharacters.Count} character(s) and {assignedGroups.Count} group(s) using it before deleting it."); + foreach (var character in assignedCharacters) + ImGui.BulletText($"{character.CharacterName} @ {character.WorldName}"); + foreach (var group in assignedGroups) + ImGui.BulletText($"{group.Name}"); + ImGui.EndTooltip(); + } + } + + ImGui.EndPopup(); + } + } + + private void DrawNewVentureList() + { + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Venture List")) + ImGui.OpenPopup("##AddList"); + + if (ImGui.BeginPopup("##AddList")) + { + (bool save, bool canSave) = DrawVentureListEditor(_newList, null); + + ImGui.BeginDisabled(!canSave); + save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save"); + ImGui.EndDisabled(); + + if (save && canSave) + { + _configuration.ItemLists.Add(new Configuration.ItemList + { + Id = Guid.NewGuid(), + Name = _newList.Name, + Type = _newList.ListType, + Priority = _newList.ListPriority, + }); + + _newList = new() + { + Name = string.Empty, + ListType = Configuration.ListType.CollectOneTime, + ListPriority = Configuration.ListPriority.InOrder + }; + + ImGui.CloseCurrentPopup(); + Save(); + } + + ImGui.EndPopup(); + } + } + + private (bool Save, bool CanSave) DrawVentureListEditor(TemporaryConfig temporaryConfig, Configuration.ItemList? list) + { + string listName = temporaryConfig.Name; + bool save = ImGui.InputTextWithHint("", "List Name...", ref listName, 64, + ImGuiInputTextFlags.EnterReturnsTrue); + bool canSave = IsValidListName(listName, list); + temporaryConfig.Name = listName; + + ImGui.PushID($"Type{list?.Id ?? Guid.Empty}"); + int type = (int)temporaryConfig.ListType; + if (ImGui.Combo("", ref type, StockingTypeLabels, StockingTypeLabels.Length)) + { + temporaryConfig.ListType = (Configuration.ListType)type; + if (temporaryConfig.ListType == Configuration.ListType.CollectOneTime) + temporaryConfig.ListPriority = Configuration.ListPriority.InOrder; + } + + ImGui.PopID(); + + if (temporaryConfig.ListType == Configuration.ListType.KeepStocked) + { + ImGui.PushID($"Priority{list?.Id ?? Guid.Empty}"); + int priority = (int)temporaryConfig.ListPriority; + if (ImGui.Combo("", ref priority, PriorityLabels, PriorityLabels.Length)) + temporaryConfig.ListPriority = (Configuration.ListPriority)priority; + ImGui.PopID(); + } + + return (save, canSave); + } + + private void DrawVentureListItemSelection(Configuration.ItemList list) + { + ImGuiEx.SetNextItemFullWidth(); + if (ImGui.BeginCombo($"##VentureSelection{list.Id}", "Add Item...")) + { + ImGuiEx.SetNextItemFullWidth(); + ImGui.InputTextWithHint("", "Filter...", ref _searchString, 256); + + foreach (var ventures in _gameCache.Ventures + .Where(x => x.Name.ToLower().Contains(_searchString.ToLower())) + .OrderBy(x => x.Level) + .ThenBy(x => x.Name) + .ThenBy(x => x.ItemId) + .GroupBy(x => x.ItemId)) + { + var venture = ventures.First(); + if (ImGui.Selectable( + $"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})##SelectVenture{venture.RowId}")) + { + list.Items.Add(new Configuration.QueuedItem + { + ItemId = venture.ItemId, + RemainingQuantity = 0, + }); + _searchString = string.Empty; + Save(); + } + } + + ImGui.EndCombo(); + } + + ImGui.Separator(); + + Configuration.QueuedItem? itemToRemove = null; + Configuration.QueuedItem? itemToAdd = null; + int indexToAdd = 0; + float windowX = ImGui.GetContentRegionAvail().X; + for (int i = 0; i < list.Items.Count; ++i) + { + var item = list.Items[i]; + ImGui.PushID($"QueueItem{i}"); + var ventures = _gameCache.Ventures.Where(x => x.ItemId == item.ItemId).ToList(); + var venture = ventures.First(); + + ImGui.SetNextItemWidth(130); + int quantity = item.RemainingQuantity; + if (ImGui.InputInt($"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})", + ref quantity, 100)) + { + item.RemainingQuantity = quantity; + Save(); + } + + ImGui.SameLine(windowX - 30); + ImGui.BeginDisabled(i == 0); + if (ImGuiComponents.IconButton($"##Up{i}", FontAwesomeIcon.ArrowUp)) + { + itemToAdd = item; + indexToAdd = i - 1; + } + ImGui.EndDisabled(); + + ImGui.SameLine(0, 0); + ImGui.BeginDisabled(i == list.Items.Count - 1); + if (ImGuiComponents.IconButton($"##Down{i}", FontAwesomeIcon.ArrowDown)) + { + itemToAdd = item; + indexToAdd = i + 1; + } + ImGui.EndDisabled(); + + ImGui.SameLine(); + if (ImGuiComponents.IconButton($"##Remove{i}", FontAwesomeIcon.Times)) + itemToRemove = item; + + ImGui.PopID(); + } + + if (itemToRemove != null) + { + list.Items.Remove(itemToRemove); + Save(); + } + + if (itemToAdd != null) + { + _pluginLog.Information($"Updating {itemToAdd.ItemId} → {indexToAdd}"); + list.Items.Remove(itemToAdd); + list.Items.Insert(indexToAdd, itemToAdd); + Save(); + } + + if (list.Items.Count > 0 && list.Type == Configuration.ListType.CollectOneTime) + { + ImGui.BeginDisabled(list.Items.All(x => x.RemainingQuantity > 0)); + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Remove all finished items")) + { + list.Items.RemoveAll(q => q.RemainingQuantity <= 0); + Save(); + } + ImGui.EndDisabled(); + ImGui.Spacing(); + } + } + private void DrawCharacters() { if (ImGui.BeginTabItem("Retainers")) @@ -290,7 +445,7 @@ internal sealed class ConfigWindow : Window // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (character.ItemListIds == null) character.ItemListIds = new(); - DrawListSelection(character.LocalContentId.ToString(), character.ItemListIds); + DrawVentureListSelection(character.LocalContentId.ToString(), character.ItemListIds); } else { @@ -301,11 +456,13 @@ internal sealed class ConfigWindow : Window var lists = group.ItemListIds .Where(listId => listId != Guid.Empty) .Select(listId => _configuration.ItemLists.SingleOrDefault(x => x.Id == listId)) + .Where(list => list != null) + .Cast() .ToList(); if (lists.Count > 0) { foreach (var list in lists) - ImGui.TextUnformatted($"{SeIconChar.LinkMarker.ToIconChar()} {list.Name}"); + ImGui.BulletText($"{list.Name}"); } else ImGui.TextColored(ImGuiColors.DalamudRed, "(None)"); @@ -354,87 +511,176 @@ internal sealed class ConfigWindow : Window { if (ImGui.BeginTabItem("Groups")) { + Configuration.CharacterGroup? groupToDelete = null; 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})")) + if (ImGuiComponents.IconButton(FontAwesomeIcon.Cog)) { - ImGui.Indent(30); - if (ImGui.BeginTabBar("GroupOptions")) + _currentEditPopups[group.Id] = new TemporaryConfig { - 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); + Name = group.Name, + }; + ImGui.OpenPopup($"##EditGroup{group.Id}"); } + DrawCharacterGroupEditorPopup(group, out var assignedCharacters, ref groupToDelete); + ImGui.SameLine(); + DrawCharacterGroup(group, assignedCharacters); + ImGui.PopID(); } - ImGui.Separator(); - - if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Group")) - ImGui.OpenPopup("##AddGroup"); - - if (ImGui.BeginPopup("##AddGroup")) + if (groupToDelete != null) { - 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(); + _configuration.CharacterGroups.Remove(groupToDelete); + Save(); } + ImGui.Separator(); + DrawNewCharacterGroup(); ImGui.EndTabItem(); } } - /* + private void DrawCharacterGroupEditorPopup(Configuration.CharacterGroup group, out List assignedCharacters, ref Configuration.CharacterGroup? groupToDelete) + { + assignedCharacters = _configuration.Characters + .Where(x => x.Type == Configuration.CharacterType.PartOfCharacterGroup && + x.CharacterGroupId == group.Id) + .OrderBy(x => x.WorldName) + .ThenBy(x => x.LocalContentId) + .ToList(); + if (_currentEditPopups.TryGetValue(group.Id, out TemporaryConfig? temporaryConfig) && ImGui.BeginPopup($"##EditGroup{group.Id}")) + { + (bool save, bool canSave) = DrawGroupEditor(temporaryConfig, group); + + ImGui.BeginDisabled(!canSave || group.Name == temporaryConfig.Name); + save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save"); + ImGui.EndDisabled(); + + if (save && canSave) + { + group.Name = temporaryConfig.Name; + + ImGui.CloseCurrentPopup(); + Save(); + } + else + { + ImGui.SameLine(); + ImGui.BeginDisabled(assignedCharacters.Count > 0); + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, "Delete")) + { + groupToDelete = group; + ImGui.CloseCurrentPopup(); + } + ImGui.EndDisabled(); + if (assignedCharacters.Count > 0 && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) + { + ImGui.BeginTooltip(); + ImGui.Text( + $"Remove the {assignedCharacters.Count} character(s) from this group before deleting it."); + foreach (var character in assignedCharacters) + ImGui.BulletText($"{character.CharacterName} @ {character.WorldName}"); + ImGui.EndTooltip(); + } + } + ImGui.EndPopup(); + } + } + + private void DrawCharacterGroup(Configuration.CharacterGroup group, List assignedCharacters) + { + 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")) + { + DrawVentureListSelection(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); + } + } + + private void DrawNewCharacterGroup() + { + if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Add Group")) + ImGui.OpenPopup("##AddGroup"); + + if (ImGui.BeginPopup("##AddGroup")) + { + (bool save, bool canSave) = DrawGroupEditor(_newGroup, null); + + ImGui.BeginDisabled(!canSave); + save |= ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Save, "Save"); + ImGui.EndDisabled(); + + if (save && canSave) + { + _configuration.CharacterGroups.Add(new Configuration.CharacterGroup + { + Id = Guid.NewGuid(), + Name = _newGroup.Name, + Icon = FontAwesomeIcon.None, + ItemListIds = new(), + }); + + _newGroup = new() { Name = string.Empty }; + + ImGui.CloseCurrentPopup(); + Save(); + } + + ImGui.EndPopup(); + } + } + + private (bool Save, bool CanSave) DrawGroupEditor(TemporaryConfig group, Configuration.CharacterGroup? existingGroup) + { + string name = group.Name; + bool save = ImGui.InputTextWithHint("", "Group Name...", ref name, 64, ImGuiInputTextFlags.EnterReturnsTrue); + bool canSave = IsValidGroupName(name, existingGroup); + + group.Name = name; + return (save, canSave); + } + + private bool IsValidGroupName(string name, Configuration.CharacterGroup? existingGroup) + { + return name.Length >= 2 && + !name.Contains('%') && + !_configuration.CharacterGroups.Any(x => x != existingGroup && name.EqualsIgnoreCase(x.Name)); + } + + private bool IsValidListName(string name, Configuration.ItemList? existingList) + { + return name.Length >= 2 && + !name.Contains('%') && + !_configuration.ItemLists.Any(x => x != existingList && name.EqualsIgnoreCase(x.Name)); + } + private void DrawGatheredItemsToCheck() { if (ImGui.BeginTabItem("Locked Items")) @@ -444,7 +690,8 @@ internal sealed class ConfigWindow : Window ImGui.Separator(); var itemsToCheck = - _configuration.QueuedItems + _configuration.ItemLists + .SelectMany(x => x.Items) .Select(x => x.ItemId) .Distinct() .Select(itemId => new @@ -462,10 +709,10 @@ internal sealed class ConfigWindow : Window .ToList(); var charactersToCheck = _configuration.Characters - .Where(x => x.Managed) + .Where(x => x.Type != Configuration.CharacterType.NotManaged) .OrderBy(x => x.WorldName) .ThenBy(x => x.LocalContentId) - .Select(x => new CheckedCharacter(x, itemsToCheck)) + .Select(x => new CheckedCharacter(_configuration, x, itemsToCheck)) .ToList(); if (_checkPerCharacter) @@ -552,9 +799,9 @@ internal sealed class ConfigWindow : Window ImGui.EndTabItem(); } - }*/ + } - private void DrawListSelection(string id, List selectedLists) + private void DrawVentureListSelection(string id, List selectedLists) { ImGui.PushID($"##ListSelection{id}"); @@ -565,17 +812,21 @@ internal sealed class ConfigWindow : Window Id = Guid.Empty, Name = "---", Type = Configuration.ListType.CollectOneTime, + Priority = Configuration.ListPriority.InOrder, } }.Concat(_configuration.ItemLists) .Select(x => (x.Id, x.Name, x)).ToList(); int? itemToRemove = null; + int? itemToAdd = null; + int indexToAdd = 0; + float windowX = ImGui.GetContentRegionAvail().X; 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); + ImGui.SetNextItemWidth(windowX - 76); if (ImGui.Combo("", ref listIndex, itemLists.Select(x => x.Name).ToArray(), itemLists.Count)) { selectedLists[i] = itemLists[listIndex].Id; @@ -583,22 +834,22 @@ internal sealed class ConfigWindow : Window } ImGui.SameLine(); - if (ImGuiComponents.IconButton($"##Jump{i}", FontAwesomeIcon.Edit)) - { - - } - - ImGui.SameLine(); + ImGui.BeginDisabled(i == 0); if (ImGuiComponents.IconButton($"##Up{i}", FontAwesomeIcon.ArrowUp)) { - + itemToAdd = i; + indexToAdd = i - 1; } + ImGui.EndDisabled(); ImGui.SameLine(0, 0); + ImGui.BeginDisabled(i == selectedLists.Count - 1); if (ImGuiComponents.IconButton($"##Down{i}", FontAwesomeIcon.ArrowDown)) { - + itemToAdd = i; + indexToAdd = i + 1; } + ImGui.EndDisabled(); ImGui.SameLine(); if (ImGuiComponents.IconButton($"##Remove{i}", FontAwesomeIcon.Times)) @@ -617,8 +868,8 @@ internal sealed class ConfigWindow : Window 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."); + ? $"{SeIconChar.BoxedNumber1.ToIconString()} Items on this list will be collected once." + : $"{SeIconChar.Circle.ToIconString()} Items on this list will be kept in stock on each character."); ImGui.Spacing(); foreach (var item in list.Items) { @@ -639,6 +890,14 @@ internal sealed class ConfigWindow : Window Save(); } + if (itemToAdd != null) + { + Guid listId = selectedLists[itemToAdd.Value]; + selectedLists.RemoveAt(itemToAdd.Value); + selectedLists.Insert(indexToAdd, listId); + 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")) @@ -669,16 +928,44 @@ internal sealed class ConfigWindow : Window private sealed class CheckedCharacter { - public CheckedCharacter(Configuration.CharacterConfiguration character, + public CheckedCharacter(Configuration configuration, Configuration.CharacterConfiguration character, List itemsToCheck) { Character = character; + List itemListIds = new(); + if (character.Type == Configuration.CharacterType.Standalone) + { + itemListIds = character.ItemListIds; + } + else if (character.Type == Configuration.CharacterType.PartOfCharacterGroup) + { + var group = configuration.CharacterGroups.SingleOrDefault(x => x.Id == character.CharacterGroupId); + if (group != null) + itemListIds = group.ItemListIds; + } + + var itemIdsOnLists = itemListIds.Where(listId => listId != Guid.Empty) + .Select(listId => configuration.ItemLists.SingleOrDefault(x => x.Id == listId)) + .Where(list => list != null) + .SelectMany(list => list!.Items) + .Select(x => x.ItemId) + .ToList(); + foreach (var item in itemsToCheck) { + // check if the item is on any relevant list + if (!itemIdsOnLists.Contains(item.ItemId)) + { + Items[item.ItemId] = ColorGrey; + continue; + } + + // check if we are the correct job bool enabled = character.Retainers.Any(x => item.Ventures.Any(v => v.MatchesJob(x.Job))); if (enabled) { + // do we have it gathered on this char? if (character.GatheredItems.Contains(item.GatheredItem.GatheredItemId)) Items[item.ItemId] = ColorGreen; else @@ -706,4 +993,11 @@ internal sealed class ConfigWindow : Window public required List Ventures { get; init; } public required uint ItemId { get; init; } } + + private sealed class TemporaryConfig + { + public required string Name { get; set; } + public Configuration.ListType ListType { get; set; } + public Configuration.ListPriority ListPriority { get; set; } + } } From 59d6f50c1863497771c530f813b824e822c43462 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Tue, 10 Oct 2023 13:26:25 +0200 Subject: [PATCH 3/8] (2.x) Add icons --- ARControl/AutoRetainerControlPlugin.cs | 8 +++- ARControl/Configuration.cs | 13 ++++++- ARControl/GameData/Venture.cs | 2 + ARControl/IconCache.cs | 53 ++++++++++++++++++++++++++ ARControl/Windows/ConfigWindow.cs | 52 +++++++++++++++++++------ 5 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 ARControl/IconCache.cs diff --git a/ARControl/AutoRetainerControlPlugin.cs b/ARControl/AutoRetainerControlPlugin.cs index dac1bd0..3b141df 100644 --- a/ARControl/AutoRetainerControlPlugin.cs +++ b/ARControl/AutoRetainerControlPlugin.cs @@ -27,12 +27,14 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin private readonly Configuration _configuration; private readonly GameCache _gameCache; + private readonly IconCache _iconCache; private readonly VentureResolver _ventureResolver; private readonly ConfigWindow _configWindow; private readonly AutoRetainerApi _autoRetainerApi; public AutoRetainerControlPlugin(DalamudPluginInterface pluginInterface, IDataManager dataManager, - IClientState clientState, IChatGui chatGui, ICommandManager commandManager, IPluginLog pluginLog) + IClientState clientState, IChatGui chatGui, ICommandManager commandManager, ITextureProvider textureProvider, + IPluginLog pluginLog) { _pluginInterface = pluginInterface; _clientState = clientState; @@ -43,9 +45,10 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin _configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration { Version = 2 }; _gameCache = new GameCache(dataManager); + _iconCache = new IconCache(textureProvider); _ventureResolver = new VentureResolver(_gameCache, _pluginLog); _configWindow = - new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _pluginLog) + new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _iconCache, _pluginLog) { IsOpen = true }; _windowSystem.AddWindow(_configWindow); @@ -184,6 +187,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin _pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle; _pluginInterface.UiBuilder.Draw -= _windowSystem.Draw; + _iconCache.Dispose(); _autoRetainerApi.Dispose(); ECommonsMain.Dispose(); } diff --git a/ARControl/Configuration.cs b/ARControl/Configuration.cs index 77bdfdf..9105f7b 100644 --- a/ARControl/Configuration.cs +++ b/ARControl/Configuration.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Dalamud.Configuration; +using Dalamud.Game.Text; using Dalamud.Interface; namespace ARControl; @@ -20,6 +21,17 @@ internal sealed class Configuration : IPluginConfiguration public required ListType Type { get; set; } = ListType.CollectOneTime; public required ListPriority Priority { get; set; } = ListPriority.InOrder; public List Items { get; set; } = new(); + + public string GetIcon() + { + return Type switch + { + ListType.CollectOneTime => SeIconChar.BoxedNumber1.ToIconString(), + ListType.KeepStocked when Priority == ListPriority.Balanced => SeIconChar.EurekaLevel.ToIconString(), + ListType.KeepStocked => SeIconChar.Circle.ToIconString(), + _ => string.Empty + }; + } } public enum ListType @@ -44,7 +56,6 @@ internal sealed class Configuration : IPluginConfiguration { public required Guid Id { get; set; } public required string Name { get; set; } - public required FontAwesomeIcon Icon { get; set; } public List ItemListIds { get; set; } = new(); } diff --git a/ARControl/GameData/Venture.cs b/ARControl/GameData/Venture.cs index 433ef57..5037cbb 100644 --- a/ARControl/GameData/Venture.cs +++ b/ARControl/GameData/Venture.cs @@ -14,6 +14,7 @@ internal sealed class Venture var taskDetails = dataManager.GetExcelSheet()!.GetRow(retainerTask.Task)!; var taskParameters = retainerTask.RetainerTaskParameter.Value!; ItemId = taskDetails.Item.Row; + IconId = taskDetails.Item.Value!.Icon; Name = taskDetails.Item.Value!.Name.ToString(); Level = retainerTask.RetainerLevel; ItemLevelCombat = retainerTask.RequiredItemLevel; @@ -76,6 +77,7 @@ internal sealed class Venture } public uint ItemId { get; } + public ushort IconId { get; } public string Name { get; } public byte Level { get; } public ushort ItemLevelCombat { get; } diff --git a/ARControl/IconCache.cs b/ARControl/IconCache.cs new file mode 100644 index 0000000..ccc9c07 --- /dev/null +++ b/ARControl/IconCache.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using Dalamud.Interface.Internal; +using Dalamud.Plugin.Services; + +namespace ARControl; + +internal sealed class IconCache : IDisposable +{ + private readonly ITextureProvider _textureProvider; + private readonly Dictionary _textureWraps = new(); + + public IconCache(ITextureProvider textureProvider) + { + _textureProvider = textureProvider; + } + + public IDalamudTextureWrap? GetIcon(uint iconId) + { + if (_textureWraps.TryGetValue(iconId, out TextureContainer? container)) + return container.Texture; + + var iconTex = _textureProvider.GetIcon(iconId); + if (iconTex != null) + { + if (iconTex.ImGuiHandle != nint.Zero) + { + _textureWraps[iconId] = new TextureContainer { Texture = iconTex }; + return iconTex; + } + + iconTex.Dispose(); + } + + _textureWraps[iconId] = new TextureContainer { Texture = null }; + return null; + } + + public void Dispose() + { + foreach (TextureContainer container in _textureWraps.Values) + container.Dispose(); + + _textureWraps.Clear(); + } + + private sealed class TextureContainer : IDisposable + { + public required IDalamudTextureWrap? Texture { get; init; } + + public void Dispose() => Texture?.Dispose(); + } +} diff --git a/ARControl/Windows/ConfigWindow.cs b/ARControl/Windows/ConfigWindow.cs index 52da773..109cfa7 100644 --- a/ARControl/Windows/ConfigWindow.cs +++ b/ARControl/Windows/ConfigWindow.cs @@ -7,6 +7,7 @@ using Dalamud.Game.Text; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; +using Dalamud.Interface.Internal; using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; @@ -18,6 +19,7 @@ namespace ARControl.Windows; internal sealed class ConfigWindow : Window { + // TODO This should also allow retainers under max level private const byte MaxLevel = 90; private static readonly Vector4 ColorGreen = ImGuiColors.HealerGreen; @@ -31,6 +33,7 @@ internal sealed class ConfigWindow : Window private readonly GameCache _gameCache; private readonly IClientState _clientState; private readonly ICommandManager _commandManager; + private readonly IconCache _iconCache; private readonly IPluginLog _pluginLog; private readonly Dictionary _currentEditPopups = new(); @@ -51,6 +54,7 @@ internal sealed class ConfigWindow : Window GameCache gameCache, IClientState clientState, ICommandManager commandManager, + IconCache iconCache, IPluginLog pluginLog) : base("ARC###ARControlConfig") { @@ -59,6 +63,7 @@ internal sealed class ConfigWindow : Window _gameCache = gameCache; _clientState = clientState; _commandManager = commandManager; + _iconCache = iconCache; _pluginLog = pluginLog; SizeConstraints = new() @@ -80,7 +85,7 @@ internal sealed class ConfigWindow : Window } } - private unsafe void DrawVentureLists() + private void DrawVentureLists() { if (ImGui.BeginTabItem("Venture Lists")) { @@ -102,7 +107,10 @@ internal sealed class ConfigWindow : Window DrawVentureListEditorPopup(list, ref listToDelete); ImGui.SameLine(); - if (ImGui.CollapsingHeader($"{list.Name} {(list.Type == Configuration.ListType.CollectOneTime ? SeIconChar.BoxedNumber1.ToIconChar() : SeIconChar.Circle.ToIconChar())}")) + + string label = $"{list.Name} {list.GetIcon()}"; + + if (ImGui.CollapsingHeader(label)) { ImGui.Indent(30); DrawVentureListItemSelection(list); @@ -257,10 +265,10 @@ internal sealed class ConfigWindow : Window private void DrawVentureListItemSelection(Configuration.ItemList list) { ImGuiEx.SetNextItemFullWidth(); - if (ImGui.BeginCombo($"##VentureSelection{list.Id}", "Add Item...")) + if (ImGui.BeginCombo($"##VentureSelection{list.Id}", "Add Venture...", ImGuiComboFlags.HeightLarge)) { ImGuiEx.SetNextItemFullWidth(); - ImGui.InputTextWithHint("", "Filter...", ref _searchString, 256); + ImGui.InputTextWithHint("", "Filter...", ref _searchString, 256, ImGuiInputTextFlags.AutoSelectAll); foreach (var ventures in _gameCache.Ventures .Where(x => x.Name.ToLower().Contains(_searchString.ToLower())) @@ -270,6 +278,14 @@ internal sealed class ConfigWindow : Window .GroupBy(x => x.ItemId)) { var venture = ventures.First(); + IDalamudTextureWrap? icon = _iconCache.GetIcon(venture.IconId); + if (icon != null) + { + ImGui.Image(icon.ImGuiHandle, new Vector2(23, 23)); + ImGui.SameLine(); + ImGui.SetCursorPosY(ImGui.GetCursorPosY() + 3); + } + if (ImGui.Selectable( $"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})##SelectVenture{venture.RowId}")) { @@ -278,7 +294,6 @@ internal sealed class ConfigWindow : Window ItemId = venture.ItemId, RemainingQuantity = 0, }); - _searchString = string.Empty; Save(); } } @@ -286,7 +301,7 @@ internal sealed class ConfigWindow : Window ImGui.EndCombo(); } - ImGui.Separator(); + ImGui.Spacing(); Configuration.QueuedItem? itemToRemove = null; Configuration.QueuedItem? itemToAdd = null; @@ -299,6 +314,13 @@ internal sealed class ConfigWindow : Window var ventures = _gameCache.Ventures.Where(x => x.ItemId == item.ItemId).ToList(); var venture = ventures.First(); + IDalamudTextureWrap? icon = _iconCache.GetIcon(venture.IconId); + if (icon != null) + { + ImGui.Image(icon.ImGuiHandle, new Vector2(23, 23)); + ImGui.SameLine(0, 3); + } + ImGui.SetNextItemWidth(130); int quantity = item.RemainingQuantity; if (ImGui.InputInt($"{venture.Name} ({string.Join(" ", ventures.Select(x => x.CategoryName))})", @@ -349,6 +371,7 @@ internal sealed class ConfigWindow : Window if (list.Items.Count > 0 && list.Type == Configuration.ListType.CollectOneTime) { + ImGui.Spacing(); ImGui.BeginDisabled(list.Items.All(x => x.RemainingQuantity > 0)); if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Remove all finished items")) { @@ -356,8 +379,8 @@ internal sealed class ConfigWindow : Window Save(); } ImGui.EndDisabled(); - ImGui.Spacing(); } + ImGui.Spacing(); } private void DrawCharacters() @@ -479,8 +502,14 @@ internal sealed class ConfigWindow : Window ImGui.BeginDisabled(retainer.Level < MaxLevel); bool managed = retainer.Managed && retainer.Level == MaxLevel; - ImGui.Text(_gameCache.Jobs[retainer.Job]); - ImGui.SameLine(); + + IDalamudTextureWrap? icon = _iconCache.GetIcon(62000 + retainer.Job); + if (icon != null) + { + ImGui.Image(icon.ImGuiHandle, new Vector2(23, 23)); + ImGui.SameLine(); + } + if (ImGui.Checkbox($"{retainer.Name}###Retainer{retainer.Name}{retainer.DisplayOrder}", ref managed)) { @@ -643,7 +672,6 @@ internal sealed class ConfigWindow : Window { Id = Guid.NewGuid(), Name = _newGroup.Name, - Icon = FontAwesomeIcon.None, ItemListIds = new(), }); @@ -868,8 +896,8 @@ internal sealed class ConfigWindow : Window var list = itemLists[listIndex].List; ImGui.Indent(30); ImGui.Text(list.Type == Configuration.ListType.CollectOneTime - ? $"{SeIconChar.BoxedNumber1.ToIconString()} Items on this list will be collected once." - : $"{SeIconChar.Circle.ToIconString()} Items on this list will be kept in stock on each character."); + ? $"{list.GetIcon()} Items on this list will be collected once." + : $"{list.GetIcon()} Items on this list will be kept in stock on each character."); ImGui.Spacing(); foreach (var item in list.Items) { From d8cfb75c33ca8974e4bb43615f65a769d072885a Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Tue, 10 Oct 2023 16:27:10 +0200 Subject: [PATCH 4/8] (2.x) Pick appropriate venture for new list types/priorities --- ARControl/ARControl.csproj | 4 + ARControl/AutoRetainerControlPlugin.Sync.cs | 15 +- ARControl/AutoRetainerControlPlugin.cs | 243 ++++++++++++++++---- ARControl/Configuration.cs | 1 + ARControl/GameData/VentureResolver.cs | 10 +- 5 files changed, 226 insertions(+), 47 deletions(-) diff --git a/ARControl/ARControl.csproj b/ARControl/ARControl.csproj index a5a4607..7134b01 100644 --- a/ARControl/ARControl.csproj +++ b/ARControl/ARControl.csproj @@ -48,6 +48,10 @@ $(DalamudLibPath)Newtonsoft.Json.dll false + + $(DalamudLibPath)FFXIVClientStructs.dll + false + $(AutoRetainerLibPath)AutoRetainerAPI.dll diff --git a/ARControl/AutoRetainerControlPlugin.Sync.cs b/ARControl/AutoRetainerControlPlugin.Sync.cs index 3f1c16b..51398fc 100644 --- a/ARControl/AutoRetainerControlPlugin.Sync.cs +++ b/ARControl/AutoRetainerControlPlugin.Sync.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; namespace ARControl; @@ -37,6 +38,7 @@ partial class AutoRetainerControlPlugin save = true; } + List seenRetainers = new(); foreach (var retainerData in offlineCharacterData.RetainerData) { var retainer = character.Retainers.SingleOrDefault(x => x.Name == retainerData.Name); @@ -52,6 +54,8 @@ partial class AutoRetainerControlPlugin character.Retainers.Add(retainer); } + seenRetainers.Add(retainer.Name); + if (retainer.DisplayOrder != retainerData.DisplayOrder) { retainer.DisplayOrder = retainerData.DisplayOrder; @@ -70,6 +74,12 @@ partial class AutoRetainerControlPlugin save = true; } + if (retainer.HasVenture != retainerData.HasVenture) + { + retainer.HasVenture = retainerData.HasVenture; + save = true; + } + if (retainer.LastVenture != retainerData.VentureID) { retainer.LastVenture = retainerData.VentureID; @@ -96,6 +106,9 @@ partial class AutoRetainerControlPlugin save = true; } } + + if (character.Retainers.RemoveAll(x => !seenRetainers.Contains(x.Name)) > 0) + save = true; } if (save) diff --git a/ARControl/AutoRetainerControlPlugin.cs b/ARControl/AutoRetainerControlPlugin.cs index 3b141df..2f1ea32 100644 --- a/ARControl/AutoRetainerControlPlugin.cs +++ b/ARControl/AutoRetainerControlPlugin.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; using ARControl.GameData; using ARControl.Windows; using AutoRetainerAPI; @@ -10,6 +12,7 @@ using Dalamud.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; using ECommons; +using FFXIVClientStructs.FFXIV.Client.Game; using ImGuiNET; namespace ARControl; @@ -17,6 +20,7 @@ namespace ARControl; [SuppressMessage("ReSharper", "UnusedType.Global")] public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin { + private const int QuickVentureId = 395; private readonly WindowSystem _windowSystem = new(nameof(AutoRetainerControlPlugin)); private readonly DalamudPluginInterface _pluginInterface; @@ -70,72 +74,199 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin } private void SendRetainerToVenture(string retainerName) + { + var venture = GetNextVenture(retainerName, false); + if (venture == QuickVentureId) + _autoRetainerApi.SetVenture(0); + else if (venture.HasValue) + _autoRetainerApi.SetVenture(venture.Value); + } + + private unsafe uint? GetNextVenture(string retainerName, bool dryRun) { var ch = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == _clientState.LocalContentId); if (ch == null) { _pluginLog.Information("No character information found"); + return null; } - else if (ch.Type == Configuration.CharacterType.NotManaged) + + if (ch.Type == Configuration.CharacterType.NotManaged) { _pluginLog.Information("Character is not managed"); + return null; } + + var retainer = ch.Retainers.SingleOrDefault(x => x.Name == retainerName); + if (retainer == null) + { + _pluginLog.Information("No retainer information found"); + return null; + } + + if (!retainer.Managed) + { + _pluginLog.Information("Retainer is not managed"); + return null; + } + + _pluginLog.Information("Checking tasks..."); + Sync(); + var venturesInProgress = CalculateVenturesInProgress(ch); + foreach (var inpr in venturesInProgress) + { + _pluginLog.Information($"In Progress: {inpr.Key} → {inpr.Value}"); + } + + IReadOnlyList itemListIds; + if (ch.Type == Configuration.CharacterType.Standalone) + itemListIds = ch.ItemListIds; else { - var retainer = ch.Retainers.SingleOrDefault(x => x.Name == retainerName); - if (retainer == null) + var group = _configuration.CharacterGroups.SingleOrDefault(x => x.Id == ch.CharacterGroupId); + if (group == null) { - _pluginLog.Information("No retainer information found"); + _pluginLog.Error($"Unable to resolve character group {ch.CharacterGroupId}."); + return null; } - else if (!retainer.Managed) + + itemListIds = group.ItemListIds; + } + + var itemLists = itemListIds.Where(listId => listId != Guid.Empty) + .Select(listId => _configuration.ItemLists.SingleOrDefault(x => x.Id == listId)) + .Where(list => list != null) + .Cast() + .ToList(); + InventoryManager* inventoryManager = InventoryManager.Instance(); + foreach (var list in itemLists) + { + _pluginLog.Information($"Checking ventures in list '{list.Name}'"); + IReadOnlyList itemsOnList; + if (list.Type == Configuration.ListType.CollectOneTime) { - _pluginLog.Information("Retainer is not managed"); + itemsOnList = list.Items + .Select(x => new StockedItem + { + QueuedItem = x, + InventoryCount = 0, + }) + .Where(x => x.RequestedCount > 0) + .ToList() + .AsReadOnly(); } else { - _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}"); - - var (venture, reward) = _ventureResolver.ResolveVenture(ch, retainer, queuedItem); - if (reward == null) + itemsOnList = list.Items + .Select(x => new StockedItem { - _pluginLog.Information("Retainer can't complete venture"); - } - else - { - _chatGui.Print( - $"[ARC] Sending retainer {retainerName} to collect {reward.Quantity}x {venture!.Name}."); - _pluginLog.Information( - $"Setting AR to use venture {venture.RowId}, which should retrieve {reward.Quantity}x {venture.Name}"); - _autoRetainerApi.SetVenture(venture.RowId); + QueuedItem = x, + InventoryCount = inventoryManager->GetInventoryItemCount(x.ItemId) + + (venturesInProgress.TryGetValue(x.ItemId, out int inProgress) ? inProgress : 0), + }) + .Where(x => x.InventoryCount <= x.RequestedCount) + .ToList() + .AsReadOnly(); - retainer.LastVenture = venture.RowId; - queuedItem.RemainingQuantity = - Math.Max(0, queuedItem.RemainingQuantity - reward.Quantity); - _pluginInterface.SavePluginConfig(_configuration); - return; - } - } - */ + // collect items with the least current inventory first + if (list.Priority == Configuration.ListPriority.Balanced) + itemsOnList = itemsOnList.OrderBy(x => x.InventoryCount).ToList().AsReadOnly(); + } - // fallback: managed but no venture found - if (retainer.LastVenture != 395) + _pluginLog.Information($"Found {itemsOnList.Count} items on current list"); + if (itemsOnList.Count == 0) + continue; + + foreach (var itemOnList in itemsOnList) + { + _pluginLog.Information($"Checking venture info for itemId {itemOnList.ItemId}"); + + var (venture, reward) = _ventureResolver.ResolveVenture(ch, retainer, itemOnList.ItemId); + if (venture == null || reward == null) { - _chatGui.Print($"[ARC] No tasks left for retainer {retainerName}, sending to Quick Venture."); - _pluginLog.Information($"No tasks left (previous venture = {retainer.LastVenture}), using QC"); - _autoRetainerApi.SetVenture(395); - - retainer.LastVenture = 395; - _pluginInterface.SavePluginConfig(_configuration); + _pluginLog.Information($"Retainer can't complete venture '{venture?.Name}'"); } else - _pluginLog.Information("Not changing venture plan, already 395"); + { + _chatGui.Print( + $"[ARC] Sending retainer {retainerName} to collect {reward.Quantity}x {venture.Name}."); + _pluginLog.Information( + $"Setting AR to use venture {venture.RowId}, which should retrieve {reward.Quantity}x {venture.Name}"); + + if (!dryRun) + { + retainer.HasVenture = true; + retainer.LastVenture = venture.RowId; + + if (list.Type == Configuration.ListType.CollectOneTime) + { + itemOnList.RequestedCount = + Math.Max(0, itemOnList.RequestedCount - reward.Quantity); + } + + _pluginInterface.SavePluginConfig(_configuration); + } + + return venture.RowId; + } + } + + } + + // fallback: managed but no venture found + if (retainer.LastVenture != QuickVentureId) + { + _chatGui.Print($"[ARC] No tasks left for retainer {retainerName}, sending to Quick Venture."); + _pluginLog.Information($"No tasks left (previous venture = {retainer.LastVenture}), using QC"); + + if (!dryRun) + { + retainer.HasVenture = true; + retainer.LastVenture = QuickVentureId; + _pluginInterface.SavePluginConfig(_configuration); + } + + return QuickVentureId; + } + else + { + _pluginLog.Information("Not changing venture, already a quick venture"); + return null; + } + } + + /// + /// This treats the retainer who is currently doing the venture as 'in-progress', since I believe the + /// relevant event is fired BEFORE the venture rewards are collected. + /// + private Dictionary CalculateVenturesInProgress(Configuration.CharacterConfiguration character) + { + Dictionary inProgress = new Dictionary(); + foreach (var retainer in character.Retainers) + { + if (retainer.Managed && retainer.HasVenture && retainer.LastVenture != 0) + { + uint ventureId = retainer.LastVenture; + if (ventureId == 0) + continue; + + var ventureForId = _gameCache.Ventures.SingleOrDefault(x => x.RowId == ventureId); + if (ventureForId == null) + continue; + + uint itemId = ventureForId.ItemId; + var (venture, reward) = _ventureResolver.ResolveVenture(character, retainer, itemId); + if (venture == null || reward == null) + continue; + + if (inProgress.TryGetValue(itemId, out int existingQuantity)) + inProgress[itemId] = reward.Quantity + existingQuantity; + else + inProgress[itemId] = reward.Quantity; } } + + return inProgress; } private void RetainerTaskButtonDraw(ulong characterId, string retainerName) @@ -174,6 +305,24 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin { if (arguments == "sync") Sync(); + else if (arguments == "d") + { + var ch = _configuration.Characters.SingleOrDefault(x => x.LocalContentId == _clientState.LocalContentId); + if (ch == null || ch.Type == Configuration.CharacterType.NotManaged || ch.Retainers.Count == 0) + { + _chatGui.PrintError("No character to debug."); + return; + } + + string retainerName = ch.Retainers.OrderBy(x => x.DisplayOrder).First().Name; + var venture = GetNextVenture(retainerName, true); + if (venture == QuickVentureId) + _chatGui.Print($"Next venture for {retainerName} is Quick Venture."); + else if (venture.HasValue) + _chatGui.Print($"Next venture for {retainerName} is {_gameCache.Ventures.First(x => x.RowId == venture.Value).Name}."); + else + _chatGui.Print($"Next venture for {retainerName} is (none)."); + } else _configWindow.Toggle(); } @@ -191,4 +340,16 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin _autoRetainerApi.Dispose(); ECommonsMain.Dispose(); } + + private sealed class StockedItem + { + public required Configuration.QueuedItem QueuedItem { get; set; } + public required int InventoryCount { get; set; } + public uint ItemId => QueuedItem.ItemId; + public int RequestedCount + { + get => QueuedItem.RemainingQuantity; + set => QueuedItem.RemainingQuantity = value; + } + } } diff --git a/ARControl/Configuration.cs b/ARControl/Configuration.cs index 9105f7b..b97fe9e 100644 --- a/ARControl/Configuration.cs +++ b/ARControl/Configuration.cs @@ -97,6 +97,7 @@ internal sealed class Configuration : IPluginConfiguration public int DisplayOrder { get; set; } public int Level { get; set; } public uint Job { get; set; } + public bool HasVenture { get; set; } public uint LastVenture { get; set; } public int ItemLevel { get; set; } public int Gathering { get; set; } diff --git a/ARControl/GameData/VentureResolver.cs b/ARControl/GameData/VentureResolver.cs index b7fa025..5b6802a 100644 --- a/ARControl/GameData/VentureResolver.cs +++ b/ARControl/GameData/VentureResolver.cs @@ -15,18 +15,18 @@ internal sealed class VentureResolver } public (Venture?, VentureReward?) ResolveVenture(Configuration.CharacterConfiguration character, - Configuration.RetainerConfiguration retainer, Configuration.QueuedItem queuedItem) + Configuration.RetainerConfiguration retainer, uint itemId) { var venture = _gameCache.Ventures .Where(x => retainer.Level >= x.Level) - .FirstOrDefault(x => x.ItemId == queuedItem.ItemId && x.MatchesJob(retainer.Job)); + .FirstOrDefault(x => x.ItemId == itemId && x.MatchesJob(retainer.Job)); if (venture == null) { - _pluginLog.Information($"No applicable venture found for itemId {queuedItem.ItemId}"); + _pluginLog.Information($"No applicable venture found for itemId {itemId}"); return (null, null); } - var itemToGather = _gameCache.ItemsToGather.FirstOrDefault(x => x.ItemId == queuedItem.ItemId); + var itemToGather = _gameCache.ItemsToGather.FirstOrDefault(x => x.ItemId == itemId); if (itemToGather != null && !character.GatheredItems.Contains(itemToGather.GatheredItemId)) { _pluginLog.Information($"Character hasn't gathered {venture.Name} yet"); @@ -34,7 +34,7 @@ internal sealed class VentureResolver } _pluginLog.Information( - $"Found venture {venture.Name}, row = {venture.RowId}, checking if it is suitable"); + $"Found venture {venture.Name}, row = {venture.RowId}, checking if we have high enough stats"); VentureReward? reward = null; if (venture.CategoryName is "MIN" or "BTN") { From 7332f9945fb5141fcf52fec75731475d46e150be Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Tue, 10 Oct 2023 16:29:44 +0200 Subject: [PATCH 5/8] Change the minimum level for retainers to work --- ARControl/AutoRetainerControlPlugin.cs | 1 - ARControl/Configuration.cs | 1 - ARControl/Windows/ConfigWindow.cs | 7 +++---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ARControl/AutoRetainerControlPlugin.cs b/ARControl/AutoRetainerControlPlugin.cs index 2f1ea32..8efe99a 100644 --- a/ARControl/AutoRetainerControlPlugin.cs +++ b/ARControl/AutoRetainerControlPlugin.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Runtime.CompilerServices; using ARControl.GameData; using ARControl.Windows; using AutoRetainerAPI; diff --git a/ARControl/Configuration.cs b/ARControl/Configuration.cs index b97fe9e..6601c7c 100644 --- a/ARControl/Configuration.cs +++ b/ARControl/Configuration.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using Dalamud.Configuration; using Dalamud.Game.Text; -using Dalamud.Interface; namespace ARControl; diff --git a/ARControl/Windows/ConfigWindow.cs b/ARControl/Windows/ConfigWindow.cs index 109cfa7..215547a 100644 --- a/ARControl/Windows/ConfigWindow.cs +++ b/ARControl/Windows/ConfigWindow.cs @@ -3,7 +3,6 @@ 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; @@ -20,7 +19,7 @@ namespace ARControl.Windows; internal sealed class ConfigWindow : Window { // TODO This should also allow retainers under max level - private const byte MaxLevel = 90; + private const byte MinLevel = 10; private static readonly Vector4 ColorGreen = ImGuiColors.HealerGreen; private static readonly Vector4 ColorRed = ImGuiColors.DalamudRed; @@ -499,9 +498,9 @@ internal sealed class ConfigWindow : Window { foreach (var retainer in character.Retainers.Where(x => x.Job > 0).OrderBy(x => x.DisplayOrder)) { - ImGui.BeginDisabled(retainer.Level < MaxLevel); + ImGui.BeginDisabled(retainer.Level < MinLevel); - bool managed = retainer.Managed && retainer.Level == MaxLevel; + bool managed = retainer.Managed && retainer.Level >= MinLevel; IDalamudTextureWrap? icon = _iconCache.GetIcon(62000 + retainer.Job); if (icon != null) From fe29867929504d694e0828ecb5cd5a437e789ca0 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Wed, 11 Oct 2023 10:19:58 +0200 Subject: [PATCH 6/8] Add LLib --- .gitmodules | 3 +++ ARControl.sln | 6 ++++++ ARControl/ARControl.csproj | 4 ++++ ARControl/Windows/ConfigWindow.cs | 3 +++ ARControl/packages.lock.json | 3 +++ LLib | 1 + 6 files changed, 20 insertions(+) create mode 100644 .gitmodules create mode 160000 LLib diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9124e51 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "LLib"] + path = LLib + url = git@git.carvel.li:liza/LLib.git diff --git a/ARControl.sln b/ARControl.sln index 1450d62..ea815c0 100644 --- a/ARControl.sln +++ b/ARControl.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ARControl", "ARControl\ARControl.csproj", "{B33BF820-56C2-45A1-AEEC-3DCF526DBF42}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLib", "LLib\LLib.csproj", "{C00249D7-E550-4A3F-937B-D938D1D46B8A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -12,5 +14,9 @@ Global {B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Debug|Any CPU.Build.0 = Debug|Any CPU {B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Release|Any CPU.ActiveCfg = Release|Any CPU {B33BF820-56C2-45A1-AEEC-3DCF526DBF42}.Release|Any CPU.Build.0 = Release|Any CPU + {C00249D7-E550-4A3F-937B-D938D1D46B8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C00249D7-E550-4A3F-937B-D938D1D46B8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C00249D7-E550-4A3F-937B-D938D1D46B8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C00249D7-E550-4A3F-937B-D938D1D46B8A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/ARControl/ARControl.csproj b/ARControl/ARControl.csproj index 7134b01..e3dfdbe 100644 --- a/ARControl/ARControl.csproj +++ b/ARControl/ARControl.csproj @@ -23,6 +23,10 @@ $(DALAMUD_HOME)/ + + + + diff --git a/ARControl/Windows/ConfigWindow.cs b/ARControl/Windows/ConfigWindow.cs index 215547a..97f3672 100644 --- a/ARControl/Windows/ConfigWindow.cs +++ b/ARControl/Windows/ConfigWindow.cs @@ -13,6 +13,7 @@ using Dalamud.Plugin.Services; using ECommons; using ECommons.ImGuiMethods; using ImGuiNET; +using LLib; namespace ARControl.Windows; @@ -74,6 +75,8 @@ internal sealed class ConfigWindow : Window public override void Draw() { + LImGui.AddPatreonIcon(_pluginInterface); + if (ImGui.BeginTabBar("ARConfigTabs")) { DrawVentureLists(); diff --git a/ARControl/packages.lock.json b/ARControl/packages.lock.json index 6cf1c73..784f99f 100644 --- a/ARControl/packages.lock.json +++ b/ARControl/packages.lock.json @@ -7,6 +7,9 @@ "requested": "[2.1.12, )", "resolved": "2.1.12", "contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg==" + }, + "llib": { + "type": "Project" } } } diff --git a/LLib b/LLib new file mode 160000 index 0000000..abbbec4 --- /dev/null +++ b/LLib @@ -0,0 +1 @@ +Subproject commit abbbec4f26b1a8903b0cd7aa04f00d557602eaf3 From fbba9244a66d0357df031f682fc31a0f5ca348f3 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Wed, 11 Oct 2023 10:38:26 +0200 Subject: [PATCH 7/8] (2.x) Improve chat feedback --- ARControl/ARControl.csproj | 3 +- ARControl/AutoRetainerControlPlugin.cs | 49 +++++++++++++++++++--- ARControl/Windows/ConfigWindow.cs | 56 ++++++++++++++++++++------ 3 files changed, 88 insertions(+), 20 deletions(-) diff --git a/ARControl/ARControl.csproj b/ARControl/ARControl.csproj index e3dfdbe..1e8538a 100644 --- a/ARControl/ARControl.csproj +++ b/ARControl/ARControl.csproj @@ -24,7 +24,7 @@ - + @@ -65,7 +65,6 @@ - diff --git a/ARControl/AutoRetainerControlPlugin.cs b/ARControl/AutoRetainerControlPlugin.cs index 8efe99a..9b6efc9 100644 --- a/ARControl/AutoRetainerControlPlugin.cs +++ b/ARControl/AutoRetainerControlPlugin.cs @@ -6,6 +6,9 @@ using ARControl.GameData; using ARControl.Windows; using AutoRetainerAPI; using Dalamud.Game.Command; +using Dalamud.Game.Text; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Interface; using Dalamud.Interface.Windowing; using Dalamud.Plugin; @@ -51,7 +54,8 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin _iconCache = new IconCache(textureProvider); _ventureResolver = new VentureResolver(_gameCache, _pluginLog); _configWindow = - new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _iconCache, _pluginLog) + new ConfigWindow(_pluginInterface, _configuration, _gameCache, _clientState, _commandManager, _iconCache, + _pluginLog) { IsOpen = true }; _windowSystem.AddWindow(_configWindow); @@ -161,7 +165,9 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin { QueuedItem = x, InventoryCount = inventoryManager->GetInventoryItemCount(x.ItemId) + - (venturesInProgress.TryGetValue(x.ItemId, out int inProgress) ? inProgress : 0), + (venturesInProgress.TryGetValue(x.ItemId, out int inProgress) + ? inProgress + : 0), }) .Where(x => x.InventoryCount <= x.RequestedCount) .ToList() @@ -188,7 +194,25 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin else { _chatGui.Print( - $"[ARC] Sending retainer {retainerName} to collect {reward.Quantity}x {venture.Name}."); + new SeString(new UIForegroundPayload(579)) + .Append(SeIconChar.Collectible.ToIconString()) + .Append(new UIForegroundPayload(0)) + .Append($" Sending retainer ") + .Append(new UIForegroundPayload(1)) + .Append(retainerName) + .Append(new UIForegroundPayload(0)) + .Append(" to collect ") + .Append(new UIForegroundPayload(1)) + .Append($"{reward.Quantity}x ") + .Append(new ItemPayload(venture.ItemId)) + .Append(venture.Name) + .Append(RawPayload.LinkTerminator) + .Append(new UIForegroundPayload(0)) + .Append(" for ") + .Append(new UIForegroundPayload(1)) + .Append($"{list.Name} {list.GetIcon()}") + .Append(new UIForegroundPayload(0)) + .Append(".")); _pluginLog.Information( $"Setting AR to use venture {venture.RowId}, which should retrieve {reward.Quantity}x {venture.Name}"); @@ -209,13 +233,24 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin return venture.RowId; } } - } // fallback: managed but no venture found if (retainer.LastVenture != QuickVentureId) { - _chatGui.Print($"[ARC] No tasks left for retainer {retainerName}, sending to Quick Venture."); + _chatGui.Print( + new SeString(new UIForegroundPayload(579)) + .Append(SeIconChar.Collectible.ToIconString()) + .Append(new UIForegroundPayload(0)) + .Append($" No tasks left for retainer ") + .Append(new UIForegroundPayload(1)) + .Append(retainerName) + .Append(new UIForegroundPayload(0)) + .Append(", sending to ") + .Append(new UIForegroundPayload(1)) + .Append("Quick Venture") + .Append(new UIForegroundPayload(0)) + .Append(".")); _pluginLog.Information($"No tasks left (previous venture = {retainer.LastVenture}), using QC"); if (!dryRun) @@ -318,7 +353,8 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin if (venture == QuickVentureId) _chatGui.Print($"Next venture for {retainerName} is Quick Venture."); else if (venture.HasValue) - _chatGui.Print($"Next venture for {retainerName} is {_gameCache.Ventures.First(x => x.RowId == venture.Value).Name}."); + _chatGui.Print( + $"Next venture for {retainerName} is {_gameCache.Ventures.First(x => x.RowId == venture.Value).Name}."); else _chatGui.Print($"Next venture for {retainerName} is (none)."); } @@ -345,6 +381,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin public required Configuration.QueuedItem QueuedItem { get; set; } public required int InventoryCount { get; set; } public uint ItemId => QueuedItem.ItemId; + public int RequestedCount { get => QueuedItem.RemainingQuantity; diff --git a/ARControl/Windows/ConfigWindow.cs b/ARControl/Windows/ConfigWindow.cs index 97f3672..ea04b5c 100644 --- a/ARControl/Windows/ConfigWindow.cs +++ b/ARControl/Windows/ConfigWindow.cs @@ -3,6 +3,7 @@ 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; @@ -26,7 +27,9 @@ internal sealed class ConfigWindow : Window private static readonly Vector4 ColorRed = ImGuiColors.DalamudRed; private static readonly Vector4 ColorGrey = ImGuiColors.DalamudGrey; private static readonly string[] StockingTypeLabels = { "Collect Once", "Keep in Stock" }; - private static readonly string[] PriorityLabels = { "Collect in order of the list", "Collect item with lowest inventory first" }; + + private static readonly string[] PriorityLabels = + { "Collect in order of the list", "Collect item with lowest inventory first" }; private readonly DalamudPluginInterface _pluginInterface; private readonly Configuration _configuration; @@ -39,12 +42,14 @@ internal sealed class ConfigWindow : Window private readonly Dictionary _currentEditPopups = new(); private string _searchString = string.Empty; private TemporaryConfig _newGroup = new() { Name = string.Empty }; + private TemporaryConfig _newList = new() { Name = string.Empty, ListType = Configuration.ListType.CollectOneTime, ListPriority = Configuration.ListPriority.InOrder }; + private bool _checkPerCharacter = true; private bool _onlyShowMissing = true; @@ -56,7 +61,7 @@ internal sealed class ConfigWindow : Window ICommandManager commandManager, IconCache iconCache, IPluginLog pluginLog) - : base("ARC###ARControlConfig") + : base($"ARC {SeIconChar.Collectible.ToIconString()}###ARControlConfig") { _pluginInterface = pluginInterface; _configuration = configuration; @@ -106,6 +111,7 @@ internal sealed class ConfigWindow : Window }; ImGui.OpenPopup($"##EditList{list.Id}"); } + DrawVentureListEditorPopup(list, ref listToDelete); ImGui.SameLine(); @@ -118,6 +124,7 @@ internal sealed class ConfigWindow : Window DrawVentureListItemSelection(list); ImGui.Unindent(30); } + ImGui.PopID(); } @@ -233,7 +240,8 @@ internal sealed class ConfigWindow : Window } } - private (bool Save, bool CanSave) DrawVentureListEditor(TemporaryConfig temporaryConfig, Configuration.ItemList? list) + private (bool Save, bool CanSave) DrawVentureListEditor(TemporaryConfig temporaryConfig, + Configuration.ItemList? list) { string listName = temporaryConfig.Name; bool save = ImGui.InputTextWithHint("", "List Name...", ref listName, 64, @@ -339,6 +347,7 @@ internal sealed class ConfigWindow : Window itemToAdd = item; indexToAdd = i - 1; } + ImGui.EndDisabled(); ImGui.SameLine(0, 0); @@ -348,6 +357,7 @@ internal sealed class ConfigWindow : Window itemToAdd = item; indexToAdd = i + 1; } + ImGui.EndDisabled(); ImGui.SameLine(); @@ -380,8 +390,10 @@ internal sealed class ConfigWindow : Window list.Items.RemoveAll(q => q.RemainingQuantity <= 0); Save(); } + ImGui.EndDisabled(); } + ImGui.Spacing(); } @@ -443,7 +455,8 @@ internal sealed class ConfigWindow : Window if (ImGui.BeginTabBar("CharOptions")) { - if (character.Type != Configuration.CharacterType.NotManaged && ImGui.BeginTabItem("Venture Lists")) + if (character.Type != Configuration.CharacterType.NotManaged && + ImGui.BeginTabItem("Venture Lists")) { int groupIndex = 0; if (character.Type == Configuration.CharacterType.PartOfCharacterGroup) @@ -461,6 +474,7 @@ internal sealed class ConfigWindow : Window character.Type = Configuration.CharacterType.PartOfCharacterGroup; character.CharacterGroupId = groups[groupIndex].Id; } + Save(); } @@ -470,14 +484,16 @@ internal sealed class ConfigWindow : Window // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (character.ItemListIds == null) character.ItemListIds = new(); - DrawVentureListSelection(character.LocalContentId.ToString(), character.ItemListIds); + DrawVentureListSelection(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 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)) @@ -495,11 +511,14 @@ internal sealed class ConfigWindow : Window 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)) + foreach (var retainer in character.Retainers.Where(x => x.Job > 0) + .OrderBy(x => x.DisplayOrder)) { ImGui.BeginDisabled(retainer.Level < MinLevel); @@ -512,7 +531,8 @@ internal sealed class ConfigWindow : Window ImGui.SameLine(); } - if (ImGui.Checkbox($"{retainer.Name}###Retainer{retainer.Name}{retainer.DisplayOrder}", + if (ImGui.Checkbox( + $"{retainer.Name}###Retainer{retainer.Name}{retainer.DisplayOrder}", ref managed)) { retainer.Managed = managed; @@ -521,8 +541,10 @@ internal sealed class ConfigWindow : Window ImGui.EndDisabled(); } + ImGui.EndTabItem(); } + ImGui.EndTabBar(); } @@ -575,7 +597,9 @@ internal sealed class ConfigWindow : Window } } - private void DrawCharacterGroupEditorPopup(Configuration.CharacterGroup group, out List assignedCharacters, ref Configuration.CharacterGroup? groupToDelete) + private void DrawCharacterGroupEditorPopup(Configuration.CharacterGroup group, + out List assignedCharacters, + ref Configuration.CharacterGroup? groupToDelete) { assignedCharacters = _configuration.Characters .Where(x => x.Type == Configuration.CharacterType.PartOfCharacterGroup && @@ -583,7 +607,8 @@ internal sealed class ConfigWindow : Window .OrderBy(x => x.WorldName) .ThenBy(x => x.LocalContentId) .ToList(); - if (_currentEditPopups.TryGetValue(group.Id, out TemporaryConfig? temporaryConfig) && ImGui.BeginPopup($"##EditGroup{group.Id}")) + if (_currentEditPopups.TryGetValue(group.Id, out TemporaryConfig? temporaryConfig) && + ImGui.BeginPopup($"##EditGroup{group.Id}")) { (bool save, bool canSave) = DrawGroupEditor(temporaryConfig, group); @@ -607,6 +632,7 @@ internal sealed class ConfigWindow : Window groupToDelete = group; ImGui.CloseCurrentPopup(); } + ImGui.EndDisabled(); if (assignedCharacters.Count > 0 && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { @@ -618,11 +644,13 @@ internal sealed class ConfigWindow : Window ImGui.EndTooltip(); } } + ImGui.EndPopup(); } } - private void DrawCharacterGroup(Configuration.CharacterGroup group, List assignedCharacters) + private void DrawCharacterGroup(Configuration.CharacterGroup group, + List assignedCharacters) { string countLabel = assignedCharacters.Count == 0 ? "no characters" : assignedCharacters.Count == 1 ? "1 character" @@ -687,7 +715,8 @@ internal sealed class ConfigWindow : Window } } - private (bool Save, bool CanSave) DrawGroupEditor(TemporaryConfig group, Configuration.CharacterGroup? existingGroup) + private (bool Save, bool CanSave) DrawGroupEditor(TemporaryConfig group, + Configuration.CharacterGroup? existingGroup) { string name = group.Name; bool save = ImGui.InputTextWithHint("", "Group Name...", ref name, 64, ImGuiInputTextFlags.EnterReturnsTrue); @@ -870,6 +899,7 @@ internal sealed class ConfigWindow : Window itemToAdd = i; indexToAdd = i - 1; } + ImGui.EndDisabled(); ImGui.SameLine(0, 0); @@ -879,6 +909,7 @@ internal sealed class ConfigWindow : Window itemToAdd = i; indexToAdd = i + 1; } + ImGui.EndDisabled(); ImGui.SameLine(); @@ -946,6 +977,7 @@ internal sealed class ConfigWindow : Window ImGui.EndPopup(); } + ImGui.EndDisabled(); ImGui.PopID(); From 2f65d7c95176c8094cdb3281bb6c6f66faceb840 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Wed, 11 Oct 2023 10:42:20 +0200 Subject: [PATCH 8/8] Replace overlay icon --- ARControl/AutoRetainerControlPlugin.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ARControl/AutoRetainerControlPlugin.cs b/ARControl/AutoRetainerControlPlugin.cs index 9b6efc9..02e7a21 100644 --- a/ARControl/AutoRetainerControlPlugin.cs +++ b/ARControl/AutoRetainerControlPlugin.cs @@ -316,9 +316,7 @@ public sealed partial class AutoRetainerControlPlugin : IDalamudPlugin return; ImGui.SameLine(); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.Text(FontAwesomeIcon.Book.ToIconString()); - ImGui.PopFont(); + ImGui.Text(SeIconChar.Collectible.ToIconString()); if (ImGui.IsItemHovered()) { string text = "This retainer is managed by ARC.";