using System; using System.Collections.Generic; using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game; using LLib.Gear; using LLib.Inventory; using Microsoft.Extensions.Logging; using Questionable.Controller.Steps.Common; using Questionable.Data; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; namespace Questionable.Controller.Steps.Shared; internal static class RedeemRewardItems { internal sealed class Factory(QuestData questData, Configuration configuration) : ITaskFactory { public unsafe IEnumerable CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step) { if (step.InteractionType != EInteractionType.AcceptQuest) { return Array.Empty(); } List list = new List(); InventoryManager* ptr = InventoryManager.Instance(); if (ptr == null) { return list; } bool hasFreeInventorySlot = InventoryHelper.HasFreeInventorySlot(); foreach (ItemReward redeemableItem in questData.RedeemableItems) { if (ptr->GetInventoryItemCount(redeemableItem.ItemId, isHq: false, checkEquipped: true, checkArmory: true, 0) != 0 && !redeemableItem.IsUnlocked() && PassesRedemptionFilters(redeemableItem.Type, redeemableItem.IsUntradable, configuration.General, hasFreeInventorySlot)) { list.Add(new Task(redeemableItem)); } } if (list.Count > 0) { list.Insert(0, new Mount.UnmountTask()); } return list; } } internal sealed record Task(ItemReward ItemReward) : ITask { public int OriginalGearsetIndex { get; set; } = -1; public override string ToString() { return "TryRedeem(" + ItemReward.Name + ")"; } } internal sealed class Executor(GameFunctions gameFunctions, Configuration configuration, ICondition condition, ILogger logger) : TaskExecutor(), IStoppableTaskExecutor, ITaskExecutor { private static readonly TimeSpan MinimumCastTime = TimeSpan.FromSeconds(4L); private const long GearsetSettleDelayMs = 1000L; private const long UseItemRetryDelayMs = 1000L; private const int MaxUseItemAttempts = 5; private long _useItemAt; private long _continueAt; private int _useItemAttempts; private bool _itemUsed; protected override bool Start() { if (condition[ConditionFlag.Mounted] && base.Task.OriginalGearsetIndex < 0) { return false; } long num = (_useItemAt = Environment.TickCount64); if (base.Task.ItemReward is CofferReward) { int? cofferGearsetIndex = configuration.General.CofferGearsetIndex; if (cofferGearsetIndex.HasValue) { int valueOrDefault = cofferGearsetIndex.GetValueOrDefault(); if (base.Task.OriginalGearsetIndex < 0) { int currentGearsetIndex = GearsetHelper.GetCurrentGearsetIndex(); if (currentGearsetIndex != valueOrDefault && GearsetHelper.TryEquipGearset(valueOrDefault)) { base.Task.OriginalGearsetIndex = currentGearsetIndex; _useItemAt = num + 1000; } } else if (GearsetHelper.GetCurrentGearsetIndex() != valueOrDefault) { GearsetHelper.TryEquipGearset(valueOrDefault); _useItemAt = num + 1000; } } } return true; } public override ETaskResult Update() { long tickCount = Environment.TickCount64; if (!_itemUsed) { if (tickCount < _useItemAt) { return ETaskResult.StillRunning; } if (base.Task.ItemReward.IsUnlocked() || !HasItemInInventory(base.Task.ItemReward.ItemId)) { RestoreGearset(); return ETaskResult.TaskComplete; } if (gameFunctions.UseItem(base.Task.ItemReward.ItemId)) { _itemUsed = true; TimeSpan timeSpan = base.Task.ItemReward.CastTime; if (timeSpan < MinimumCastTime) { timeSpan = MinimumCastTime; } _continueAt = tickCount + (long)timeSpan.TotalMilliseconds + 3000; return ETaskResult.StillRunning; } _useItemAttempts++; if (_useItemAttempts >= 5) { logger.LogWarning("Unable to use {ItemName} after {Attempts} attempts, giving up", base.Task.ItemReward.Name, _useItemAttempts); RestoreGearset(); return ETaskResult.TaskComplete; } _useItemAt = tickCount + 1000; return ETaskResult.StillRunning; } if (condition[ConditionFlag.Casting]) { return ETaskResult.StillRunning; } if (tickCount <= _continueAt) { return ETaskResult.StillRunning; } RestoreGearset(); return ETaskResult.TaskComplete; } public void StopNow() { RestoreGearset(); } private void RestoreGearset() { if (base.Task.OriginalGearsetIndex >= 0 && GearsetHelper.TryEquipGearset(base.Task.OriginalGearsetIndex)) { base.Task.OriginalGearsetIndex = -1; } } private unsafe static bool HasItemInInventory(uint itemId) { InventoryManager* ptr = InventoryManager.Instance(); if (ptr != null) { return ptr->GetInventoryItemCount(itemId, isHq: false, checkEquipped: true, checkArmory: true, 0) > 0; } return false; } public override bool ShouldInterruptOnDamage() { return true; } } internal static bool PassesRedemptionFilters(EItemRewardType type, bool isUntradable, Configuration.GeneralConfiguration general, bool hasFreeInventorySlot) { if (general.DisabledRewardTypes.Contains(type)) { return false; } if (general.SkipTradableRewards && !isUntradable) { return false; } if (type == EItemRewardType.Coffer && !hasFreeInventorySlot) { return false; } return true; } }