diff --git a/Deliveroo/Configuration.cs b/Deliveroo/Configuration.cs index 1b8e888..4caf366 100644 --- a/Deliveroo/Configuration.cs +++ b/Deliveroo/Configuration.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Dalamud.Configuration; +using Dalamud.Game.Text; using Deliveroo.GameData; namespace Deliveroo; @@ -29,6 +30,23 @@ internal sealed class Configuration : IPluginConfiguration public uint ItemId { get; set; } public int Limit { get; set; } public bool Enabled { get; set; } = true; + public PurchaseType Type { get; set; } = PurchaseType.KeepStocked; + + public string GetIcon() + { + return Type switch + { + PurchaseType.PurchaseOneTime => SeIconChar.BoxedNumber1.ToIconString(), + PurchaseType.KeepStocked => SeIconChar.Circle.ToIconString(), + _ => SeIconChar.BoxedQuestionMark.ToIconString(), + }; + } + } + + public enum PurchaseType + { + PurchaseOneTime, + KeepStocked, } public bool AddVentureIfNoItemToPurchaseSelected() diff --git a/Deliveroo/Deliveroo.csproj b/Deliveroo/Deliveroo.csproj index 1ccaeee..b702d33 100644 --- a/Deliveroo/Deliveroo.csproj +++ b/Deliveroo/Deliveroo.csproj @@ -1,7 +1,7 @@ net7.0-windows - 2.26 + 3.0 11.0 enable true diff --git a/Deliveroo/DeliverooPlugin.Exchange.cs b/Deliveroo/DeliverooPlugin.Exchange.cs index de6a4da..7f804ea 100644 --- a/Deliveroo/DeliverooPlugin.Exchange.cs +++ b/Deliveroo/DeliverooPlugin.Exchange.cs @@ -38,8 +38,16 @@ partial class DeliverooPlugin toBuy = Math.Min(toBuy, 99); } - if (GetItemCount(request.ItemId) + toBuy < request.EffectiveLimit) - return request; + if (request.Type == Configuration.PurchaseType.KeepStocked) + { + if (GetItemCount(request.ItemId) + toBuy < request.EffectiveLimit) + return request; + } + else + { + if (toBuy < request.EffectiveLimit) + return request; + } } return null; @@ -139,7 +147,10 @@ partial class DeliverooPlugin { _pluginLog.Information($"Selecting item {itemId}, {i}"); long toBuy = (GetCurrentSealCount() - EffectiveReservedSealCount) / item.SealCost; - toBuy = Math.Min(toBuy, item.EffectiveLimit - GetItemCount(item.ItemId)); + if (item.Type == Configuration.PurchaseType.KeepStocked) + toBuy = Math.Min(toBuy, item.EffectiveLimit - GetItemCount(item.ItemId)); + else + toBuy = Math.Min(toBuy, item.EffectiveLimit); if (item.ItemId != ItemIds.Venture && !_configuration.IgnoreCertainLimitations) toBuy = Math.Min(toBuy, 99); @@ -150,6 +161,7 @@ partial class DeliverooPlugin return false; } + item.TemporaryPurchaseQuantity = toBuy; _chatGui.Print(new SeString(new TextPayload($"Buying {toBuy}x ")) .Append(SeString.CreateItemLink(item.ItemId)) .Append(new TextPayload("..."))); diff --git a/Deliveroo/DeliverooPlugin.SelectYesNo.cs b/Deliveroo/DeliverooPlugin.SelectYesNo.cs index c0eca53..21fa09d 100644 --- a/Deliveroo/DeliverooPlugin.SelectYesNo.cs +++ b/Deliveroo/DeliverooPlugin.SelectYesNo.cs @@ -27,9 +27,12 @@ partial class DeliverooPlugin return; } - _pluginLog.Information($"Selecting 'yes' ({text})"); + _pluginLog.Information($"Selecting 'yes' ({text}) (callback = {item.OnPurchase}, qty = {item.TemporaryPurchaseQuantity})"); addonSelectYesNo->AtkUnitBase.FireCallbackInt(0); + item.OnPurchase?.Invoke((int)item.TemporaryPurchaseQuantity); + item.TemporaryPurchaseQuantity = 0; + var nextItem = GetNextItemToPurchase(item); if (nextItem != null && GetCurrentSealCount() >= EffectiveReservedSealCount + nextItem.SealCost) CurrentStage = Stage.SelectRewardTier; diff --git a/Deliveroo/PurchaseItemRequest.cs b/Deliveroo/PurchaseItemRequest.cs index 5985578..f372518 100644 --- a/Deliveroo/PurchaseItemRequest.cs +++ b/Deliveroo/PurchaseItemRequest.cs @@ -1,4 +1,5 @@ -using Deliveroo.GameData; +using System; +using Deliveroo.GameData; namespace Deliveroo; @@ -6,9 +7,13 @@ internal sealed class PurchaseItemRequest { public required uint ItemId { get; init; } public required string Name { get; set; } - public required uint EffectiveLimit { get; init; } + public required uint EffectiveLimit { get; set; } public required uint SealCost { get; init; } public required RewardTier Tier { get; init; } public required RewardSubCategory SubCategory { get; init; } public required uint StackSize { get; init; } + public required Configuration.PurchaseType Type { get; init; } + + public Action? OnPurchase { get; set; } + public long TemporaryPurchaseQuantity { get; set; } } diff --git a/Deliveroo/Windows/TurnInWindow.cs b/Deliveroo/Windows/TurnInWindow.cs index 76ee495..9b2d669 100644 --- a/Deliveroo/Windows/TurnInWindow.cs +++ b/Deliveroo/Windows/TurnInWindow.cs @@ -20,7 +20,7 @@ namespace Deliveroo.Windows; internal sealed class TurnInWindow : LImGui.LWindow { - private readonly IReadOnlyList _inventoryTypes = new[] + private static readonly IReadOnlyList InventoryTypes = new[] { InventoryType.Inventory1, InventoryType.Inventory2, @@ -40,6 +40,8 @@ internal sealed class TurnInWindow : LImGui.LWindow InventoryType.EquippedItems }.AsReadOnly(); + private static readonly string[] StockingTypeLabels = { "Purchase Once", "Keep in Stock" }; + private readonly DeliverooPlugin _plugin; private readonly DalamudPluginInterface _pluginInterface; private readonly Configuration _configuration; @@ -116,22 +118,38 @@ internal sealed class TurnInWindow : LImGui.LWindow return ItemsWrapper.GetItemsToPurchase() .Where(x => x.ItemId != GcRewardItem.None.ItemId) .Where(x => x.Enabled) + .Where(x => x.Type == Configuration.PurchaseType.KeepStocked || x.Limit > 0) .Select(x => new { Item = x, Reward = _gcRewardsCache.GetReward(x.ItemId) }) .Where(x => x.Reward.GrandCompanies.Contains(grandCompany)) .Where(x => x.Reward.RequiredRank <= rank) - .Select(x => new PurchaseItemRequest + .Select(x => { - ItemId = x.Item.ItemId, - Name = x.Reward.Name, - EffectiveLimit = CalculateEffectiveLimit( - x.Item.ItemId, - x.Item.Limit <= 0 ? uint.MaxValue : (uint)x.Item.Limit, - x.Reward.StackSize, - x.Reward.InventoryLimit), - SealCost = x.Reward.SealCost, - Tier = x.Reward.Tier, - SubCategory = x.Reward.SubCategory, - StackSize = x.Reward.StackSize, + var request = new PurchaseItemRequest + { + ItemId = x.Item.ItemId, + Name = x.Reward.Name, + EffectiveLimit = CalculateEffectiveLimit( + x.Item.ItemId, + x.Item.Limit <= 0 ? uint.MaxValue : (uint)x.Item.Limit, + x.Reward.StackSize, + x.Reward.InventoryLimit), + SealCost = x.Reward.SealCost, + Tier = x.Reward.Tier, + SubCategory = x.Reward.SubCategory, + StackSize = x.Reward.StackSize, + Type = x.Item.Type, + }; + if (x.Item.Type == Configuration.PurchaseType.PurchaseOneTime) + { + request.OnPurchase = qty => + { + request.EffectiveLimit -= (uint)qty; + x.Item.Limit -= qty; + ItemsWrapper.Save(); + }; + } + + return request; }) .ToList(); } @@ -153,7 +171,8 @@ internal sealed class TurnInWindow : LImGui.LWindow ImGui.TextColored(ImGuiColors.DalamudRed, "You do not have the required rank for Expert Delivery."); return; } - else if (_configuration.BehaviorOnOtherWorld == Configuration.EBehaviorOnOtherWorld.DisableTurnIn && !IsOnHomeWorld) + else if (_configuration.BehaviorOnOtherWorld == Configuration.EBehaviorOnOtherWorld.DisableTurnIn && + !IsOnHomeWorld) { State = false; ImGui.TextColored(ImGuiColors.DalamudRed, "You are not on your home world."); @@ -248,17 +267,40 @@ internal sealed class TurnInWindow : LImGui.LWindow for (int i = 0; i < itemsWrapper.GetItemsToPurchase().Count; ++i) { ImGui.PushID($"ItemToBuy{i}"); - var item = itemsWrapper.GetItemsToPurchase()[i]; + Configuration.PurchasePriority item = itemsWrapper.GetItemsToPurchase()[i]; + float indentX = ImGui.GetCursorPosX(); bool enabled = item.Enabled; - ImGui.PushID($"Enable{i}"); - if (ImGui.Checkbox("", ref enabled)) + int popColors = 0; + if (!enabled) { - item.Enabled = enabled; - itemsWrapper.Save(); + ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0.5f, 0.35f, 1f)); + popColors++; } - ImGui.PopID(); + if (ImGui.Button($"{item.GetIcon()}")) + ImGui.OpenPopup($"Configure{i}"); + + ImGui.PopStyleColor(popColors); + + if (ImGui.BeginPopup($"Configure{i}")) + { + if (ImGui.Checkbox($"Enabled##Enabled{i}", ref enabled)) + { + item.Enabled = enabled; + itemsWrapper.Save(); + } + + ImGui.SetNextItemWidth(150 * ImGuiHelpers.GlobalScale); + int type = (int)item.Type; + if (ImGui.Combo($"##Type{i}", ref type, StockingTypeLabels, StockingTypeLabels.Length)) + { + item.Type = (Configuration.PurchaseType)type; + itemsWrapper.Save(); + } + + ImGui.EndPopup(); + } ImGui.SameLine(0, 3); ImGui.BeginDisabled(!enabled); @@ -280,6 +322,8 @@ internal sealed class TurnInWindow : LImGui.LWindow ImGui.SameLine(0, 3); } + indentX = ImGui.GetCursorPosX() - indentX; + if (ImGui.Combo("", ref comboValueIndex, comboValues.Select(x => x.Name).ToArray(), comboValues.Count)) { comboItem = comboValues[comboValueIndex]; @@ -318,13 +362,16 @@ internal sealed class TurnInWindow : LImGui.LWindow if (enabled) { - ImGui.Indent(52); + ImGui.Indent(indentX); if (comboValueIndex > 0) { ImGui.SetNextItemWidth(ImGuiHelpers.GlobalScale * 130); int limit = Math.Min(item.Limit, (int)comboItem.Item.InventoryLimit); int stepSize = comboItem.Item.StackSize < 99 ? 1 : 50; - if (ImGui.InputInt("Maximum items to buy", ref limit, stepSize, stepSize * 10)) + string label = item.Type == Configuration.PurchaseType.KeepStocked + ? "Maximum items to buy" + : "Remaining items to buy"; + if (ImGui.InputInt(label, ref limit, stepSize, stepSize * 10)) { item.Limit = Math.Min(Math.Max(0, limit), (int)comboItem.Item.InventoryLimit); itemsWrapper.Save(); @@ -350,7 +397,7 @@ internal sealed class TurnInWindow : LImGui.LWindow } } - ImGui.Unindent(52); + ImGui.Unindent(indentX); } ImGui.PopID(); @@ -403,7 +450,7 @@ internal sealed class TurnInWindow : LImGui.LWindow { uint slotsThatCanBeUsed = 0; InventoryManager* inventoryManager = InventoryManager.Instance(); - foreach (var inventoryType in _inventoryTypes) + foreach (var inventoryType in InventoryTypes) { var container = inventoryManager->GetInventoryContainer(inventoryType); for (int i = 0; i < container->Size; ++i)