using System; using Dalamud.Plugin.Services; using LLib.Shop; using Microsoft.Extensions.Logging; using Questionable.Model.Questing; namespace Questionable.Controller.GameUi; internal sealed class GCShopHandler : IDisposable { private enum GCShopState { Inactive, Purchasing, Completed } private readonly QuestController _questController; private readonly GrandCompanyShop _gcShop; private readonly IObjectTable _objectTable; private readonly IFramework _framework; private readonly ILogger _logger; private GCShopState _state; private GCPurchaseInfo? _currentPurchase; private DateTime _lastActionTime = DateTime.MinValue; private bool _purchaseCompletedThisStep; public bool IsActive => _state != GCShopState.Inactive; public GCShopHandler(QuestController questController, GrandCompanyShop gcShop, IObjectTable objectTable, IFramework framework, ILogger logger) { _questController = questController; _gcShop = gcShop; _objectTable = objectTable; _framework = framework; _logger = logger; _gcShop.PurchaseCompleted += OnPurchaseCompleted; _framework.Update += OnFrameworkUpdate; } public void Dispose() { _framework.Update -= OnFrameworkUpdate; _gcShop.PurchaseCompleted -= OnPurchaseCompleted; } private void OnFrameworkUpdate(IFramework framework) { if (_objectTable.LocalPlayer == null) { return; } if (!_questController.IsRunning) { if (_state != GCShopState.Inactive) { Reset(); } return; } CheckForGCPurchase(); if (_state == GCShopState.Purchasing) { HandlePurchasing(); } else if (_state == GCShopState.Completed) { _logger.LogInformation("GC shop purchase completed!"); _state = GCShopState.Inactive; _lastActionTime = DateTime.MinValue; } } private void CheckForGCPurchase() { QuestController.QuestProgress currentQuest = _questController.CurrentQuest; if (currentQuest == null) { if (_currentPurchase != null) { Reset(); } return; } GCPurchaseInfo gCPurchaseInfo = (currentQuest.Quest.FindSequence(currentQuest.Sequence)?.FindStep(currentQuest.Step))?.GCPurchase; if (gCPurchaseInfo == null) { if (_currentPurchase != null) { _currentPurchase = null; _purchaseCompletedThisStep = false; } if (_gcShop.IsOpen) { _logger.LogDebug("GC shop is open but current step has no GCPurchase. Quest={QuestId}, Seq={Sequence}, Step={Step}", currentQuest.Quest.Id, currentQuest.Sequence, currentQuest.Step); } } else { if (_currentPurchase != gCPurchaseInfo) { _currentPurchase = gCPurchaseInfo; _purchaseCompletedThisStep = false; } if (_gcShop.IsOpen && _state == GCShopState.Inactive && !_purchaseCompletedThisStep) { _logger.LogInformation("GC shop is open. Starting purchase of item {ItemId}.", gCPurchaseInfo.ItemId); _state = GCShopState.Purchasing; } } } private void HandlePurchasing() { if (_gcShop.IsOpen && _currentPurchase != null && !((DateTime.Now - _lastActionTime).TotalMilliseconds < 500.0)) { if (!_gcShop.IsPurchaseInProgress) { _gcShop.StartPurchase(_currentPurchase.ItemId, _currentPurchase.RankTab, _currentPurchase.CategoryTab); _lastActionTime = DateTime.Now; } _gcShop.ProcessPurchase(); } } private void OnPurchaseCompleted(object? sender, PurchaseCompletedEventArgs e) { if (_state == GCShopState.Purchasing && _currentPurchase != null && _questController.IsRunning && e.Success && e.ItemId == _currentPurchase.ItemId) { _logger.LogInformation("GC purchase of item {ItemId} completed successfully", e.ItemId); _gcShop.CloseShop(); _purchaseCompletedThisStep = true; _state = GCShopState.Completed; } } private void Reset() { _state = GCShopState.Inactive; _currentPurchase = null; _lastActionTime = DateTime.MinValue; _purchaseCompletedThisStep = false; } }