diff --git a/Deliveroo/Configuration.cs b/Deliveroo/Configuration.cs new file mode 100644 index 0000000..26c28d7 --- /dev/null +++ b/Deliveroo/Configuration.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using Dalamud.Configuration; + +namespace Deliveroo; + +internal sealed class Configuration : IPluginConfiguration +{ + public int Version { get; set; } = 1; + + public List ItemsAvailableForPurchase { get; set; } = new(); + public uint SelectedPurchaseItemId { get; set; } = 0; +} diff --git a/Deliveroo/Deliveroo.csproj b/Deliveroo/Deliveroo.csproj index 615ab88..4c49b59 100644 --- a/Deliveroo/Deliveroo.csproj +++ b/Deliveroo/Deliveroo.csproj @@ -56,6 +56,7 @@ + diff --git a/Deliveroo/Deliveroo.json b/Deliveroo/Deliveroo.json index ab2ccbd..3816de3 100644 --- a/Deliveroo/Deliveroo.json +++ b/Deliveroo/Deliveroo.json @@ -1,7 +1,7 @@ { "Name": "Deliveroo", "Author": "Liza Carvelli", - "Punchline": "", + "Punchline": "Better Grand Company Deliveries", "Description": "", "RepoUrl": "https://git.carvel.li/liza/Deliveroo" } diff --git a/Deliveroo/DeliverooPlugin.cs b/Deliveroo/DeliverooPlugin.cs index 7ace4b9..bc96ed8 100644 --- a/Deliveroo/DeliverooPlugin.cs +++ b/Deliveroo/DeliverooPlugin.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Runtime.InteropServices; +using Dalamud.Data; using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; @@ -13,6 +14,8 @@ using Dalamud.Interface.Windowing; using Dalamud.Logging; using Dalamud.Memory; using Dalamud.Plugin; +using Deliveroo.GameData; +using Deliveroo.Windows; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Game.UI; @@ -24,7 +27,7 @@ using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; namespace Deliveroo; -public class DeliverooPlugin : IDalamudPlugin +public sealed class DeliverooPlugin : IDalamudPlugin { private readonly WindowSystem _windowSystem = new(typeof(DeliverooPlugin).AssemblyQualifiedName); @@ -36,14 +39,20 @@ public class DeliverooPlugin : IDalamudPlugin private readonly ObjectTable _objectTable; private readonly TargetManager _targetManager; + // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable + private readonly Configuration _configuration; + // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable + private readonly GcRewardsCache _gcRewardsCache; + private readonly ConfigWindow _configWindow; private readonly TurnInWindow _turnInWindow; private Stage _currentStageInternal = Stage.Stop; private DateTime _continueAt = DateTime.MinValue; - private SealPurchaseOption? _selectedPurchaseOption; + private GcRewardItem _selectedRewardItem = GcRewardItem.None; public DeliverooPlugin(DalamudPluginInterface pluginInterface, ChatGui chatGui, GameGui gameGui, - Framework framework, ClientState clientState, ObjectTable objectTable, TargetManager targetManager) + Framework framework, ClientState clientState, ObjectTable objectTable, TargetManager targetManager, + DataManager dataManager) { _pluginInterface = pluginInterface; _chatGui = chatGui; @@ -53,11 +62,16 @@ public class DeliverooPlugin : IDalamudPlugin _objectTable = objectTable; _targetManager = targetManager; - _turnInWindow = new TurnInWindow(); + _configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration(); + _gcRewardsCache = new GcRewardsCache(dataManager); + _configWindow = new ConfigWindow(_pluginInterface, this, _configuration, _gcRewardsCache); + _windowSystem.AddWindow(_configWindow); + _turnInWindow = new TurnInWindow(this, _pluginInterface, _configuration, _gcRewardsCache); _windowSystem.AddWindow(_turnInWindow); _framework.Update += FrameworkUpdate; _pluginInterface.UiBuilder.Draw += _windowSystem.Draw; + _pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle; } public string Name => "Deliveroo"; @@ -80,9 +94,12 @@ public class DeliverooPlugin : IDalamudPlugin { if (!_clientState.IsLoggedIn || _clientState.TerritoryType is not 128 and not 130 and not 132 || GetDistanceToNpc(GetQuartermasterId(), out GameObject? quartermaster) >= 7f || - GetDistanceToNpc(GetPersonnelOfficerId(), out GameObject? personnelOfficer) >= 7f) + GetDistanceToNpc(GetPersonnelOfficerId(), out GameObject? personnelOfficer) >= 7f || + _configWindow.IsOpen) { _turnInWindow.IsOpen = false; + _turnInWindow.State = false; + CurrentStage = Stage.Stop; } else if (DateTime.Now > _continueAt) { @@ -98,10 +115,11 @@ public class DeliverooPlugin : IDalamudPlugin if (_turnInWindow.State && CurrentStage == Stage.Stop) { CurrentStage = Stage.TargetPersonnelOfficer; - _selectedPurchaseOption = _turnInWindow.SelectedOption; - if (_selectedPurchaseOption.ItemId == 0) - _selectedPurchaseOption = null; - else if (GetCurrentSealCount() > GetSealCap() / 2) + _selectedRewardItem = _turnInWindow.SelectedItem; + if (_selectedRewardItem.IsValid() && _selectedRewardItem.RequiredRank > GetGrandCompanyRank()) + _selectedRewardItem = GcRewardItem.None; + + if (_selectedRewardItem.IsValid() && GetCurrentSealCount() > GetSealCap() / 2) CurrentStage = Stage.TargetQuartermaster; if (TryGetAddonByName("GrandCompanySupplyList", out var gcSupplyList) && @@ -145,7 +163,7 @@ public class DeliverooPlugin : IDalamudPlugin break; var agent = (AgentGrandCompanySupply*)agentInterface; - List items = BuildTurnInList(agent); + List items = BuildTurnInList(agent); if (items.Count == 0 || addon->UldManager.NodeList[20]->IsVisible) { CurrentStage = Stage.CloseGcSupplyThenStop; @@ -212,7 +230,7 @@ public class DeliverooPlugin : IDalamudPlugin case Stage.CloseGcSupply: if (SelectSelectString(3)) { - if (_selectedPurchaseOption == null) + if (!_selectedRewardItem.IsValid()) { _turnInWindow.State = false; CurrentStage = Stage.Stop; @@ -230,12 +248,12 @@ public class DeliverooPlugin : IDalamudPlugin case Stage.CloseGcSupplyThenStop: if (SelectSelectString(3)) { - if (_selectedPurchaseOption == null) + if (!_selectedRewardItem.IsValid()) { _turnInWindow.State = false; CurrentStage = Stage.Stop; } - else if (GetCurrentSealCount() <= 2000 + _selectedPurchaseOption!.SealCost) + else if (GetCurrentSealCount() <= 2000 + _selectedRewardItem.SealCost) { _turnInWindow.State = false; CurrentStage = Stage.Stop; @@ -260,18 +278,19 @@ public class DeliverooPlugin : IDalamudPlugin break; InteractWithTarget(quartermaster!); - CurrentStage = Stage.SelectRewardRank; + CurrentStage = Stage.SelectRewardTier; break; - case Stage.SelectRewardRank: + case Stage.SelectRewardTier: { if (TryGetAddonByName("GrandCompanyExchange", out var addonExchange) && IsAddonReady(addonExchange)) { + PluginLog.Information($"Selecting tier 1, {(int)_selectedRewardItem.Tier - 1}"); var selectRank = stackalloc AtkValue[] { new() { Type = ValueType.Int, Int = 1 }, - new() { Type = ValueType.Int, Int = (int)_selectedPurchaseOption!.Rank }, + new() { Type = ValueType.Int, Int = (int)_selectedRewardItem.Tier - 1 }, new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 }, @@ -282,21 +301,22 @@ public class DeliverooPlugin : IDalamudPlugin }; addonExchange->FireCallback(9, selectRank); _continueAt = DateTime.Now.AddSeconds(0.5); - CurrentStage = Stage.SelectRewardType; + CurrentStage = Stage.SelectRewardSubCategory; } break; } - case Stage.SelectRewardType: + case Stage.SelectRewardSubCategory: { if (TryGetAddonByName("GrandCompanyExchange", out var addonExchange) && IsAddonReady(addonExchange)) { + PluginLog.Information($"Selecting subcategory 2, {(int)_selectedRewardItem.SubCategory}"); var selectType = stackalloc AtkValue[] { new() { Type = ValueType.Int, Int = 2 }, - new() { Type = ValueType.Int, Int = (int)_selectedPurchaseOption!.Type }, + new() { Type = ValueType.Int, Int = (int)_selectedRewardItem.SubCategory }, new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 }, new() { Type = 0, Int = 0 }, @@ -315,38 +335,20 @@ public class DeliverooPlugin : IDalamudPlugin case Stage.SelectReward: { - // coke: 0i, 31i, 5i[count], unknown, true, false, unknown, unknown, unknown if (TryGetAddonByName("GrandCompanyExchange", out var addonExchange) && IsAddonReady(addonExchange)) { - int toBuy = (GetCurrentSealCount() - 2000) / _selectedPurchaseOption!.SealCost; - bool isVenture = _selectedPurchaseOption!.ItemId == 21072; - if (isVenture) - toBuy = Math.Min(toBuy, 65000 - GetCurrentVentureCount()); - - if (toBuy == 0) + if (SelectRewardItem(addonExchange)) { - _turnInWindow.State = false; - CurrentStage = Stage.Stop; - break; + _continueAt = DateTime.Now.AddSeconds(0.5); + CurrentStage = Stage.ConfirmReward; } - - _chatGui.Print($"Buying {toBuy}x {_selectedPurchaseOption!.Name}..."); - var selectReward = stackalloc AtkValue[] + else { - new() { Type = ValueType.Int, Int = 0 }, - new() { Type = ValueType.Int, Int = _selectedPurchaseOption!.Position }, - new() { Type = ValueType.Int, Int = toBuy }, - new() { Type = 0, Int = 0 }, - new() { Type = ValueType.Bool, Byte = 1 }, - new() { Type = ValueType.Bool, Byte = 0 }, - new() { Type = 0, Int = 0 }, - new() { Type = 0, Int = 0 }, - new() { Type = 0, Int = 0 } - }; - addonExchange->FireCallback(9, selectReward); - _continueAt = DateTime.Now.AddSeconds(0.5); - CurrentStage = Stage.ConfirmReward; + PluginLog.Warning("Could not find selected reward item"); + _continueAt = DateTime.Now.AddSeconds(0.5); + CurrentStage = Stage.CloseGcExchange; + } } break; @@ -407,19 +409,20 @@ public class DeliverooPlugin : IDalamudPlugin public void Dispose() { + _pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle; _pluginInterface.UiBuilder.Draw -= _windowSystem.Draw; _framework.Update -= FrameworkUpdate; } - private unsafe List BuildTurnInList(AgentGrandCompanySupply* agent) + private unsafe List BuildTurnInList(AgentGrandCompanySupply* agent) { - List list = new(); + List list = new(); for (int i = 11 /* skip over provisioning items */; i < agent->NumItems; ++i) { GrandCompanyItem item = agent->ItemArray[i]; // this includes all items, even if they don't match the filter - list.Add(new GcItem + list.Add(new TurnInItem { ItemId = Marshal.ReadInt32(new nint(&item) + 132), Name = MemoryHelper.ReadSeString(&item.ItemName).ToString(), @@ -490,6 +493,48 @@ public class DeliverooPlugin : IDalamudPlugin return addon->IsVisible && addon->UldManager.LoadedState == AtkLoadState.Loaded; } + private unsafe bool SelectRewardItem(AtkUnitBase* addonExchange) + { + uint itemsOnCurrentPage = addonExchange->AtkValues[1].UInt; + for (uint i = 0; i < itemsOnCurrentPage; ++i) + { + uint itemId = addonExchange->AtkValues[317 + i].UInt; + if (itemId == _selectedRewardItem.ItemId) + { + long toBuy = (GetCurrentSealCount() - 2000) / _selectedRewardItem.SealCost; + bool isVenture = _selectedRewardItem.ItemId == ItemIds.Venture; + if (isVenture) + toBuy = Math.Min(toBuy, 65000 - GetCurrentVentureCount()); + + if (toBuy == 0) + { + _turnInWindow.State = false; + CurrentStage = Stage.Stop; + break; + } + + PluginLog.Information($"Selecting item {itemId}, {i}"); + _chatGui.Print($"Buying {toBuy}x {_selectedRewardItem.Name}..."); + var selectReward = stackalloc AtkValue[] + { + new() { Type = ValueType.Int, Int = 0 }, + new() { Type = ValueType.Int, Int = (int)i }, + new() { Type = ValueType.Int, Int = (int)toBuy }, + new() { Type = 0, Int = 0 }, + new() { Type = ValueType.Bool, Byte = 1 }, + new() { Type = ValueType.Bool, Byte = 0 }, + new() { Type = 0, Int = 0 }, + new() { Type = 0, Int = 0 }, + new() { Type = 0, Int = 0 } + }; + addonExchange->FireCallback(9, selectReward); + return true; + } + } + + return false; + } + private unsafe int GetCurrentSealCount() { InventoryManager* inventoryManager = InventoryManager.Instance(); @@ -506,9 +551,13 @@ public class DeliverooPlugin : IDalamudPlugin } } - private unsafe int GetPersonnelOfficerId() + internal unsafe GrandCompany GetGrandCompany() => (GrandCompany)PlayerState.Instance()->GrandCompany; + + internal unsafe byte GetGrandCompanyRank() => PlayerState.Instance()->GetGrandCompanyRank(); + + private int GetPersonnelOfficerId() { - return ((GrandCompany)PlayerState.Instance()->GrandCompany) switch + return GetGrandCompany() switch { GrandCompany.Maelstrom => 0xF4B94, GrandCompany.ImmortalFlames => 0xF4B97, @@ -517,9 +566,9 @@ public class DeliverooPlugin : IDalamudPlugin }; } - private unsafe int GetQuartermasterId() + private int GetQuartermasterId() { - return ((GrandCompany)PlayerState.Instance()->GrandCompany) switch + return GetGrandCompany() switch { GrandCompany.Maelstrom => 0xF4B93, GrandCompany.ImmortalFlames => 0xF4B96, @@ -528,9 +577,9 @@ public class DeliverooPlugin : IDalamudPlugin }; } - private unsafe int GetSealCap() + private int GetSealCap() { - return PlayerState.Instance()->GetGrandCompanyRank() switch + return GetGrandCompanyRank() switch { 1 => 10_000, 2 => 15_000, @@ -550,7 +599,7 @@ public class DeliverooPlugin : IDalamudPlugin private unsafe int GetCurrentVentureCount() { InventoryManager* inventoryManager = InventoryManager.Instance(); - return inventoryManager->GetInventoryItemCount(21072, false, false, false); + return inventoryManager->GetInventoryItemCount(ItemIds.Venture, false, false, false); } private unsafe bool SelectSelectString(int choice) @@ -570,6 +619,9 @@ public class DeliverooPlugin : IDalamudPlugin if (TryGetAddonByName("SelectYesno", out var addonSelectYesno) && IsAddonReady(&addonSelectYesno->AtkUnitBase)) { + PluginLog.Information( + $"Selecting choice={choice} for '{MemoryHelper.ReadSeString(&addonSelectYesno->PromptText->NodeText)}'"); + addonSelectYesno->AtkUnitBase.FireCallbackInt(choice); return true; } @@ -616,8 +668,8 @@ public class DeliverooPlugin : IDalamudPlugin CloseGcSupplyThenStop, TargetQuartermaster, - SelectRewardRank, - SelectRewardType, + SelectRewardTier, + SelectRewardSubCategory, SelectReward, ConfirmReward, CloseGcExchange, diff --git a/Deliveroo/GameData/GcRewardItem.cs b/Deliveroo/GameData/GcRewardItem.cs new file mode 100644 index 0000000..ec83cd9 --- /dev/null +++ b/Deliveroo/GameData/GcRewardItem.cs @@ -0,0 +1,57 @@ +using System; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + +namespace Deliveroo.GameData; + +internal sealed class GcRewardItem : IEquatable +{ + public static GcRewardItem None { get; } = new GcRewardItem + { + ItemId = 0, + Name = "Buy Nothing", + GrandCompany = GrandCompany.None, + Tier = RewardTier.First, + SubCategory = RewardSubCategory.Unknown, + RequiredRank = 0, + StackSize = 0, + SealCost = 100_000, + }; + + public required uint ItemId { get; init; } + public required string Name { get; init; } + public required GrandCompany GrandCompany { get; init; } + public required RewardTier Tier { get; init; } + public required RewardSubCategory SubCategory { get; init; } + public required uint RequiredRank { get; init; } + public required uint StackSize { get; init; } + public required uint SealCost { get; init; } + + public bool IsValid() => ItemId > 0 && GrandCompany != GrandCompany.None; + + public bool Equals(GcRewardItem? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return ItemId == other.ItemId; + } + + public override bool Equals(object? obj) + { + return ReferenceEquals(this, obj) || obj is GcRewardItem other && Equals(other); + } + + public override int GetHashCode() + { + return (int)ItemId; + } + + public static bool operator ==(GcRewardItem? left, GcRewardItem? right) + { + return Equals(left, right); + } + + public static bool operator !=(GcRewardItem? left, GcRewardItem? right) + { + return !Equals(left, right); + } +} diff --git a/Deliveroo/GameData/GcRewardsCache.cs b/Deliveroo/GameData/GcRewardsCache.cs new file mode 100644 index 0000000..e8ded0b --- /dev/null +++ b/Deliveroo/GameData/GcRewardsCache.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Data.SqlTypes; +using System.Linq; +using Dalamud; +using Dalamud.Data; +using Dalamud.Logging; +using Lumina.Excel.GeneratedSheets; +using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany; + +namespace Deliveroo.GameData; + +internal class GcRewardsCache +{ + public GcRewardsCache(DataManager dataManager) + { + var categories = dataManager.GetExcelSheet()! + .Where(x => x.RowId > 0) + .ToDictionary(x => x.RowId, + x => + (Gc: (GrandCompany)x.GrandCompany.Row, + Tier: (RewardTier)x.Tier, + SubCategory: (RewardSubCategory)x.SubCategory)); + + var items = dataManager.GetExcelSheet()! + .Where(x => x.RowId > 0 && x.Item.Row > 0) + .ToList(); + + foreach (var item in items) + { + var category = categories[item.RowId]; + Rewards[category.Gc].Add(new GcRewardItem + { + ItemId = item.Item.Row, + Name = item.Item.Value!.Name.ToString(), + GrandCompany = category.Gc, + Tier = category.Tier, + SubCategory = category.SubCategory, + RequiredRank = item.RequiredGrandCompanyRank.Row, + StackSize = item.Item!.Value.StackSize, + SealCost = item.CostGCSeals, + }); + } + } + + public Dictionary> Rewards { get; } = new() + { + { GrandCompany.Maelstrom, new() }, + { GrandCompany.TwinAdder, new() }, + { GrandCompany.ImmortalFlames, new() } + }; +} diff --git a/Deliveroo/GameData/ItemIds.cs b/Deliveroo/GameData/ItemIds.cs new file mode 100644 index 0000000..6230251 --- /dev/null +++ b/Deliveroo/GameData/ItemIds.cs @@ -0,0 +1,6 @@ +namespace Deliveroo.GameData; + +public static class ItemIds +{ + public const uint Venture = 21072; +} diff --git a/Deliveroo/GameData/RewardSubCategory.cs b/Deliveroo/GameData/RewardSubCategory.cs new file mode 100644 index 0000000..42ce0b6 --- /dev/null +++ b/Deliveroo/GameData/RewardSubCategory.cs @@ -0,0 +1,10 @@ +namespace Deliveroo.GameData; + +public enum RewardSubCategory : int +{ + Unknown = 0, + Materiel = 1, + Weapons = 2, + Armor = 3, + Materials = 4, +} diff --git a/Deliveroo/GameData/RewardTier.cs b/Deliveroo/GameData/RewardTier.cs new file mode 100644 index 0000000..a5a5dac --- /dev/null +++ b/Deliveroo/GameData/RewardTier.cs @@ -0,0 +1,9 @@ +namespace Deliveroo.GameData; + +public enum RewardTier : int +{ + Unknown = 0, + First = 1, + Second = 2, + Third = 3, +} diff --git a/Deliveroo/GcItem.cs b/Deliveroo/GameData/TurnInItem.cs similarity index 85% rename from Deliveroo/GcItem.cs rename to Deliveroo/GameData/TurnInItem.cs index a375108..28758a7 100644 --- a/Deliveroo/GcItem.cs +++ b/Deliveroo/GameData/TurnInItem.cs @@ -1,9 +1,9 @@ using System.Runtime.InteropServices; using FFXIVClientStructs.FFXIV.Client.System.String; -namespace Deliveroo; +namespace Deliveroo.GameData; -internal sealed class GcItem +internal sealed class TurnInItem { public required int ItemId { get; init; } public required string Name { get; init; } diff --git a/Deliveroo/SealPurchaseOption.cs b/Deliveroo/SealPurchaseOption.cs deleted file mode 100644 index 371b610..0000000 --- a/Deliveroo/SealPurchaseOption.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Deliveroo; - -internal sealed class SealPurchaseOption -{ - public SealPurchaseOption(uint itemId, string name, PurchaseRank rank, PurchaseType type, int position, - int sealCost) - { - ItemId = itemId; - Name = name; - Rank = rank; - Type = type; - Position = position; - SealCost = sealCost; - } - - public uint ItemId { get; } - public string Name { get; } - public PurchaseRank Rank { get; } - public PurchaseType Type { get; } - public int Position { get; } - public int SealCost { get; } - - public enum PurchaseRank : int - { - First = 0, - Second = 1, - Third = 2, - Unknown = int.MaxValue, - } - - public enum PurchaseType : int - { - Materiel = 1, - Weapons = 2, - Armor = 3, - Materials = 4, - Unknown = int.MaxValue, - } -} diff --git a/Deliveroo/TurnInWindow.cs b/Deliveroo/TurnInWindow.cs deleted file mode 100644 index 9d243bf..0000000 --- a/Deliveroo/TurnInWindow.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using Dalamud.Interface.Colors; -using Dalamud.Interface.Windowing; -using FFXIVClientStructs.FFXIV.Client.Game; -using ImGuiNET; - -namespace Deliveroo; - -internal sealed class TurnInWindow : Window -{ - private int _sealPurchaseSelected = 0; - - private readonly List _sealPurchaseOptions = new() - { - new SealPurchaseOption(0, "Buy Nothing", SealPurchaseOption.PurchaseRank.Unknown, - SealPurchaseOption.PurchaseType.Unknown, 0, 0), - new SealPurchaseOption(21072, "Venture", SealPurchaseOption.PurchaseRank.First, - SealPurchaseOption.PurchaseType.Materiel, 0, 200), - new SealPurchaseOption(5530, "Coke", SealPurchaseOption.PurchaseRank.Third, - SealPurchaseOption.PurchaseType.Materials, 31, 200), - }; - - public TurnInWindow() - : base("GC Delivery###DeliverooTurnIn") - { - Position = new Vector2(100, 100); - PositionCondition = ImGuiCond.FirstUseEver; - - Flags = ImGuiWindowFlags.AlwaysAutoResize; - } - - public bool State { get; set; } - public decimal Multiplier { private get; set; } - public SealPurchaseOption SelectedOption => _sealPurchaseOptions[_sealPurchaseSelected]; - - public string Debug { get; set; } - - public override void Draw() - { - bool state = State; - if (ImGui.Checkbox("Handle GC turn ins/exchange automatically", ref state)) - { - State = state; - } - - ImGui.Indent(27); - if (Multiplier == 1m) - { - ImGui.TextColored(ImGuiColors.DalamudRed, "You do not have a buff active"); - } - else - { - ImGui.TextColored(ImGuiColors.HealerGreen, $"Current Buff: {(Multiplier - 1m) * 100:N0}%%"); - } - - ImGui.Spacing(); - ImGui.BeginDisabled(state); - string[] comboValues = _sealPurchaseOptions.Select(x => - { - if (x.ItemId == 0) - return x.Name; - - return $"{x.Name} ({GetItemCount(x.ItemId):N0}x)"; - }).ToArray(); - ImGui.Combo("", ref _sealPurchaseSelected, comboValues, comboValues.Length); - ImGui.EndDisabled(); - - ImGui.Unindent(27); - - ImGui.Separator(); - ImGui.Text($"Debug (State): {Debug}"); - } - - private unsafe int GetItemCount(uint itemId) - { - InventoryManager* inventoryManager = InventoryManager.Instance(); - return inventoryManager->GetInventoryItemCount(itemId, false, false, false); - } -} diff --git a/Deliveroo/Windows/ConfigWindow.cs b/Deliveroo/Windows/ConfigWindow.cs new file mode 100644 index 0000000..0c9f792 --- /dev/null +++ b/Deliveroo/Windows/ConfigWindow.cs @@ -0,0 +1,139 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Dalamud.Interface.Windowing; +using Dalamud.Logging; +using Dalamud.Plugin; +using Deliveroo.GameData; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; +using ImGuiNET; + +namespace Deliveroo.Windows; + +internal sealed class ConfigWindow : Window +{ + private readonly DalamudPluginInterface _pluginInterface; + private readonly DeliverooPlugin _plugin; + private readonly Configuration _configuration; + private readonly GcRewardsCache _gcRewardsCache; + + private readonly Dictionary _itemLookup; + private uint _dragDropSource = 0; + + public ConfigWindow(DalamudPluginInterface pluginInterface, DeliverooPlugin plugin, Configuration configuration, + GcRewardsCache gcRewardsCache) + : base("Deliveroo - Configuration###DeliverooConfig") + { + _pluginInterface = pluginInterface; + _plugin = plugin; + _configuration = configuration; + _gcRewardsCache = gcRewardsCache; + + _itemLookup = _gcRewardsCache.Rewards.Values + .SelectMany(x => x) + .Distinct() + .ToDictionary(x => x.ItemId, x => x); + + Size = new Vector2(400, 300); + SizeCondition = ImGuiCond.Appearing; + } + + public override unsafe void Draw() + { + ImGui.Text("Items available for Auto-Buy:"); + uint? itemToRemove = null; + uint? itemToAdd = null; + int indexToAdd = 0; + + if (_configuration.ItemsAvailableForPurchase.Count == 0) + { + _configuration.ItemsAvailableForPurchase.Add(ItemIds.Venture); + _pluginInterface.SavePluginConfig(_configuration); + } + + if (ImGui.BeginChild("Items", new Vector2(-1, -30), true, ImGuiWindowFlags.NoSavedSettings)) + { + for (int i = 0; i < _configuration.ItemsAvailableForPurchase.Count; ++i) + { + uint itemId = _configuration.ItemsAvailableForPurchase[i]; + ImGui.PushID($"###Item{i}"); + ImGui.BeginDisabled(_configuration.ItemsAvailableForPurchase.Count == 1 && itemId == ItemIds.Venture); + + ImGui.Selectable(_itemLookup[itemId].Name); + + if (ImGui.BeginDragDropSource()) + { + ImGui.SetDragDropPayload("DeliverooDragDrop", nint.Zero, 0); + _dragDropSource = itemId; + + ImGui.EndDragDropSource(); + } + + if (ImGui.BeginDragDropTarget() && + _dragDropSource > 0 && + ImGui.AcceptDragDropPayload("DeliverooDragDrop").NativePtr != null) + { + itemToAdd = _dragDropSource; + indexToAdd = i; + + ImGui.EndDragDropTarget(); + _dragDropSource = 0; + } + + ImGui.OpenPopupOnItemClick($"###ctx{i}", ImGuiPopupFlags.MouseButtonRight); + if (ImGui.BeginPopup($"###ctx{i}")) + { + if (ImGui.Selectable($"Remove {_itemLookup[itemId].Name}")) + itemToRemove = itemId; + + ImGui.EndPopup(); + } + + ImGui.EndDisabled(); + ImGui.PopID(); + } + } + + ImGui.EndChild(); + + if (itemToRemove != null) + { + _configuration.ItemsAvailableForPurchase.Remove(itemToRemove.Value); + _pluginInterface.SavePluginConfig(_configuration); + } + + if (itemToAdd != null) + { + _configuration.ItemsAvailableForPurchase.Remove(itemToAdd.Value); + _configuration.ItemsAvailableForPurchase.Insert(indexToAdd, itemToAdd.Value); + _pluginInterface.SavePluginConfig(_configuration); + } + + if (_plugin.GetGrandCompany() != GrandCompany.None) + { + List<(uint ItemId, string Name)> comboValues = _gcRewardsCache.Rewards[_plugin.GetGrandCompany()] + .Where(x => x.SubCategory == RewardSubCategory.Materials || x.SubCategory == RewardSubCategory.Materiel) + .Where(x => x.StackSize > 1) + .Where(x => !_configuration.ItemsAvailableForPurchase.Contains(x.ItemId)) + .Select(x => (x.ItemId, x.Name)) + .OrderBy(x => x.Name) + .ThenBy(x => x.GetHashCode()) + .ToList(); + comboValues.Insert(0, (0, "")); + + int currentItem = 0; + if (ImGui.Combo("Add Item", ref currentItem, comboValues.Select(x => x.Name).ToArray(), comboValues.Count)) + { + _configuration.ItemsAvailableForPurchase.Add(comboValues[currentItem].ItemId); + _pluginInterface.SavePluginConfig(_configuration); + } + } + else + { + int currentItem = 0; + ImGui.Combo("Add Item", ref currentItem, new string[] { "(Not part of a GC)" }, 1); + } + + ImGui.Unindent(30); + } +} diff --git a/Deliveroo/Windows/TurnInWindow.cs b/Deliveroo/Windows/TurnInWindow.cs new file mode 100644 index 0000000..7f0cb19 --- /dev/null +++ b/Deliveroo/Windows/TurnInWindow.cs @@ -0,0 +1,138 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Dalamud.Interface.Colors; +using Dalamud.Interface.Windowing; +using Dalamud.Logging; +using Dalamud.Plugin; +using Deliveroo.GameData; +using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; +using ImGuiNET; + +namespace Deliveroo.Windows; + +internal sealed class TurnInWindow : Window +{ + private readonly DeliverooPlugin _plugin; + private readonly DalamudPluginInterface _pluginInterface; + private readonly Configuration _configuration; + private readonly GcRewardsCache _gcRewardsCache; + private int _selectedAutoBuyItem = 0; + + public TurnInWindow(DeliverooPlugin plugin, DalamudPluginInterface pluginInterface, Configuration configuration, + GcRewardsCache gcRewardsCache) + : base("GC Delivery###DeliverooTurnIn") + { + _plugin = plugin; + _pluginInterface = pluginInterface; + _configuration = configuration; + _gcRewardsCache = gcRewardsCache; + + Position = new Vector2(100, 100); + PositionCondition = ImGuiCond.FirstUseEver; + + Flags = ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoCollapse; + ShowCloseButton = false; + } + + public bool State { get; set; } + public decimal Multiplier { private get; set; } + + public uint SelectedItemId + { + get + { + if (_selectedAutoBuyItem == 0 || _selectedAutoBuyItem > _configuration.ItemsAvailableForPurchase.Count) + return 0; + + return _configuration.ItemsAvailableForPurchase[_selectedAutoBuyItem - 1]; + } + set + { + int index = _configuration.ItemsAvailableForPurchase.IndexOf(value); + if (index >= 0) + _selectedAutoBuyItem = index + 1; + else + _selectedAutoBuyItem = 0; + } + } + + public GcRewardItem SelectedItem + { + get + { + uint selectedItemId = SelectedItemId; + if (selectedItemId == 0) + return GcRewardItem.None; + + return _gcRewardsCache.Rewards[_plugin.GetGrandCompany()].Single(x => x.ItemId == selectedItemId); + } + } + + public string Debug { get; set; } + + public override void OnOpen() + { + SelectedItemId = _configuration.SelectedPurchaseItemId; + } + + public override void Draw() + { + bool state = State; + if (ImGui.Checkbox("Handle GC turn ins/exchange automatically", ref state)) + { + State = state; + } + + ImGui.Indent(27); + if (Multiplier == 1m) + { + ImGui.TextColored(ImGuiColors.DalamudRed, "You do not have a buff active"); + } + else + { + ImGui.TextColored(ImGuiColors.HealerGreen, $"Current Buff: {(Multiplier - 1m) * 100:N0}%%"); + } + + GrandCompany grandCompany = _plugin.GetGrandCompany(); + if (grandCompany != GrandCompany.None) // not sure we should ever get here + { + ImGui.Spacing(); + ImGui.BeginDisabled(state); + + List comboValues = new() { "Buy Nothing" }; + foreach (var itemId in _configuration.ItemsAvailableForPurchase) + { + var name = _gcRewardsCache.Rewards[grandCompany].First(x => x.ItemId == itemId).Name; + int itemCount = GetItemCount(itemId); + if (itemCount > 0) + comboValues.Add($"{name} ({itemCount:N0})"); + else + comboValues.Add(name); + } + + if (ImGui.Combo("", ref _selectedAutoBuyItem, comboValues.ToArray(), comboValues.Count)) + { + _configuration.SelectedPurchaseItemId = SelectedItemId; + _pluginInterface.SavePluginConfig(_configuration); + } + + if (SelectedItem.IsValid() && SelectedItem.RequiredRank > _plugin.GetGrandCompanyRank()) + ImGui.TextColored(ImGuiColors.DalamudRed, "Your rank isn't high enough to buy this item."); + + ImGui.EndDisabled(); + } + + ImGui.Unindent(27); + + ImGui.Separator(); + ImGui.Text($"Debug (State): {Debug}"); + } + + private unsafe int GetItemCount(uint itemId) + { + InventoryManager* inventoryManager = InventoryManager.Instance(); + return inventoryManager->GetInventoryItemCount(itemId, false, false, false); + } +}