256 lines
6 KiB
C#
256 lines
6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Application.Network.WorkDefinitions;
|
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
|
|
namespace QuestionableCompanion.Services;
|
|
|
|
public class QuestDetectionService : IDisposable
|
|
{
|
|
private readonly IFramework framework;
|
|
|
|
private readonly IPluginLog log;
|
|
|
|
private readonly IClientState clientState;
|
|
|
|
private readonly HashSet<uint> acceptedQuests = new HashSet<uint>();
|
|
|
|
private readonly HashSet<uint> completedQuests = new HashSet<uint>();
|
|
|
|
private HashSet<uint> completedQuestCache = new HashSet<uint>();
|
|
|
|
private DateTime lastCacheRefresh = DateTime.MinValue;
|
|
|
|
private const int CACHE_REFRESH_MINUTES = 5;
|
|
|
|
public event Action<uint, string>? QuestAccepted;
|
|
|
|
public event Action<uint, string>? QuestCompleted;
|
|
|
|
public QuestDetectionService(IFramework framework, IPluginLog log, IClientState clientState)
|
|
{
|
|
this.framework = framework;
|
|
this.log = log;
|
|
this.clientState = clientState;
|
|
framework.Update += OnFrameworkUpdate;
|
|
log.Information("[QuestDetection] Service initialized");
|
|
}
|
|
|
|
private void OnFrameworkUpdate(IFramework framework)
|
|
{
|
|
if (!clientState.IsLoggedIn)
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
CheckQuestUpdates();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Debug("[QuestDetection] Error in framework update: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
private unsafe void CheckQuestUpdates()
|
|
{
|
|
QuestManager* questManager = QuestManager.Instance();
|
|
if (questManager == null)
|
|
{
|
|
log.Debug("[QuestDetection] QuestManager instance is null");
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
Span<QuestWork> normalQuests = questManager->NormalQuests;
|
|
if (normalQuests.Length == 0)
|
|
{
|
|
log.Debug("[QuestDetection] NormalQuests array is empty");
|
|
return;
|
|
}
|
|
int maxSlots = Math.Min(normalQuests.Length, 30);
|
|
for (int i = 0; i < maxSlots; i++)
|
|
{
|
|
try
|
|
{
|
|
QuestWork quest = normalQuests[i];
|
|
if (quest.QuestId == 0)
|
|
{
|
|
continue;
|
|
}
|
|
uint questId = quest.QuestId;
|
|
if (!acceptedQuests.Contains(questId))
|
|
{
|
|
if (!IsQuestComplete(questId))
|
|
{
|
|
acceptedQuests.Add(questId);
|
|
string questName = GetQuestName(questId);
|
|
log.Information($"[QuestDetection] Quest Accepted: {questId} - {questName}");
|
|
this.QuestAccepted?.Invoke(questId, questName);
|
|
}
|
|
}
|
|
else if (!completedQuests.Contains(questId) && IsQuestComplete(questId))
|
|
{
|
|
completedQuests.Add(questId);
|
|
string questName2 = GetQuestName(questId);
|
|
log.Information($"[QuestDetection] Quest Completed: {questId} - {questName2}");
|
|
this.QuestCompleted?.Invoke(questId, questName2);
|
|
}
|
|
}
|
|
catch (IndexOutOfRangeException)
|
|
{
|
|
log.Debug($"[QuestDetection] Index {i} out of range, stopping quest check");
|
|
break;
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
log.Debug($"[QuestDetection] Error checking quest slot {i}: {ex2.Message}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex3)
|
|
{
|
|
log.Warning("[QuestDetection] Error accessing quest data: " + ex3.Message);
|
|
}
|
|
}
|
|
|
|
private bool IsQuestComplete(uint questId)
|
|
{
|
|
try
|
|
{
|
|
return QuestManager.IsQuestComplete(questId);
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public unsafe bool IsQuestCompletedDirect(uint questId)
|
|
{
|
|
try
|
|
{
|
|
if (QuestManager.Instance() == null)
|
|
{
|
|
log.Warning("[QuestDetection] QuestManager instance not available");
|
|
return false;
|
|
}
|
|
bool isComplete = QuestManager.IsQuestComplete(questId);
|
|
log.Debug($"[QuestDetection] Quest {questId} completion status: {isComplete}");
|
|
return isComplete;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error($"[QuestDetection] Failed to check quest {questId}: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public unsafe List<uint> GetAllCompletedQuestIds()
|
|
{
|
|
List<uint> completed = new List<uint>();
|
|
try
|
|
{
|
|
if (QuestManager.Instance() == null)
|
|
{
|
|
log.Warning("[QuestDetection] QuestManager instance not available");
|
|
return completed;
|
|
}
|
|
log.Information("[QuestDetection] Scanning for completed quests...");
|
|
foreach (var item in new List<(uint, uint)>
|
|
{
|
|
(1u, 3000u),
|
|
(65000u, 71000u)
|
|
})
|
|
{
|
|
uint start = item.Item1;
|
|
uint end = item.Item2;
|
|
for (uint i = start; i <= end; i++)
|
|
{
|
|
try
|
|
{
|
|
if (QuestManager.IsQuestComplete(i))
|
|
{
|
|
completed.Add(i);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
log.Information($"[QuestDetection] Retrieved {completed.Count} completed quests");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("[QuestDetection] Error while fetching completed quests: " + ex.Message);
|
|
}
|
|
return completed;
|
|
}
|
|
|
|
public void RefreshQuestCache()
|
|
{
|
|
try
|
|
{
|
|
log.Information("[QuestDetection] Refreshing quest cache...");
|
|
List<uint> allCompleted = GetAllCompletedQuestIds();
|
|
completedQuestCache = new HashSet<uint>(allCompleted);
|
|
lastCacheRefresh = DateTime.Now;
|
|
log.Information($"[QuestDetection] Quest cache refreshed with {completedQuestCache.Count} completed quests");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("[QuestDetection] Failed to refresh quest cache: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
public bool IsQuestCompletedCached(uint questId)
|
|
{
|
|
if (completedQuestCache.Count == 0 || (DateTime.Now - lastCacheRefresh).TotalMinutes > 5.0)
|
|
{
|
|
RefreshQuestCache();
|
|
}
|
|
return completedQuestCache.Contains(questId);
|
|
}
|
|
|
|
private string GetQuestName(uint questId)
|
|
{
|
|
try
|
|
{
|
|
return $"Quest {questId}";
|
|
}
|
|
catch
|
|
{
|
|
return $"Quest {questId}";
|
|
}
|
|
}
|
|
|
|
public void ResetTracking()
|
|
{
|
|
acceptedQuests.Clear();
|
|
completedQuests.Clear();
|
|
completedQuestCache.Clear();
|
|
lastCacheRefresh = DateTime.MinValue;
|
|
log.Information("[QuestDetection] Tracking reset");
|
|
}
|
|
|
|
public bool IsQuestAccepted(uint questId)
|
|
{
|
|
return acceptedQuests.Contains(questId);
|
|
}
|
|
|
|
public bool IsQuestCompleted(uint questId)
|
|
{
|
|
return completedQuests.Contains(questId);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
framework.Update -= OnFrameworkUpdate;
|
|
acceptedQuests.Clear();
|
|
completedQuests.Clear();
|
|
completedQuestCache.Clear();
|
|
log.Information("[QuestDetection] Service disposed");
|
|
}
|
|
}
|