qstbak/QuestionableCompanion/QuestionableCompanion.Services/ARRTrialAutomationService.cs
2025-12-07 10:54:53 +10:00

393 lines
9.8 KiB
C#

using System;
using System.Threading.Tasks;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.Text;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
namespace QuestionableCompanion.Services;
public class ARRTrialAutomationService : IDisposable
{
private readonly IPluginLog log;
private readonly IFramework framework;
private readonly ICommandManager commandManager;
private readonly IChatGui chatGui;
private readonly Configuration config;
private readonly QuestionableIPC questionableIPC;
private readonly SubmarineManager submarineManager;
private readonly HelperManager helperManager;
private readonly IPartyList partyList;
private readonly ICondition condition;
private readonly MemoryHelper memoryHelper;
private bool isInDuty;
private static readonly (uint QuestId, uint TrialId, string ADCommand, string Name)[] Trials = new(uint, uint, string, string)[3]
{
(1048u, 20004u, "/ad run trial 292 1", "Ifrit HM"),
(1157u, 20006u, "/ad run trial 294 1", "Garuda HM"),
(1158u, 20005u, "/ad run trial 293 1", "Titan HM")
};
private const uint TRIGGER_QUEST = 89u;
private const uint TARGET_QUEST = 363u;
private bool isProcessing;
private int currentTrialIndex = -1;
private bool waitingForQuest;
private bool waitingForParty;
private bool waitingForTrial;
private DateTime lastCheckTime = DateTime.MinValue;
public ARRTrialAutomationService(IPluginLog log, IFramework framework, ICommandManager commandManager, IChatGui chatGui, Configuration config, QuestionableIPC questionableIPC, SubmarineManager submarineManager, HelperManager helperManager, IPartyList partyList, ICondition condition, MemoryHelper memoryHelper)
{
this.log = log;
this.framework = framework;
this.commandManager = commandManager;
this.chatGui = chatGui;
this.config = config;
this.questionableIPC = questionableIPC;
this.submarineManager = submarineManager;
this.helperManager = helperManager;
this.partyList = partyList;
this.condition = condition;
this.memoryHelper = memoryHelper;
framework.Update += OnFrameworkUpdate;
condition.ConditionChange += OnConditionChanged;
log.Information("[ARRTrials] Service initialized");
}
private void OnFrameworkUpdate(IFramework framework)
{
if (!isProcessing)
{
return;
}
if (waitingForParty && partyList != null && partyList.Length > 1)
{
if (!((DateTime.Now - lastCheckTime).TotalSeconds < 1.0))
{
lastCheckTime = DateTime.Now;
log.Information($"[ARRTrials] Party join detected (Size: {partyList.Length}) - Triggering trial...");
waitingForParty = false;
TriggerCurrentTrial();
}
}
else if (waitingForQuest && currentTrialIndex >= 0 && currentTrialIndex < Trials.Length && !((DateTime.Now - lastCheckTime).TotalSeconds < 2.0))
{
lastCheckTime = DateTime.Now;
(uint QuestId, uint TrialId, string ADCommand, string Name) tuple = Trials[currentTrialIndex];
uint trialId = tuple.TrialId;
string name = tuple.Name;
bool unlocked = IsTrialUnlocked(trialId);
log.Debug($"[ARRTrials] Polling {name} ({trialId}) Unlocked: {unlocked}");
if (unlocked)
{
log.Information("[ARRTrials] Polling detected " + name + " unlocked - Proceeding...");
waitingForQuest = false;
helperManager.InviteHelpers();
waitingForParty = true;
}
}
}
public bool IsTrialComplete(uint instanceId)
{
return UIState.IsInstanceContentCompleted(instanceId);
}
public bool IsTrialUnlocked(uint instanceId)
{
return UIState.IsInstanceContentUnlocked(instanceId);
}
public bool IsTargetQuestAvailableOrComplete()
{
if (QuestManager.IsQuestComplete(363u))
{
return true;
}
if (questionableIPC.IsReadyToAcceptQuest(363u.ToString()))
{
return true;
}
return false;
}
public void OnTriggerQuestComplete()
{
if (!config.EnableARRPrimalCheck)
{
log.Debug("[ARRTrials] Feature disabled, skipping check");
return;
}
log.Information("[ARRTrials] Quest 89 complete, starting ARR Primal check...");
StartTrialChain();
}
public void StartTrialChain()
{
if (isProcessing)
{
log.Debug("[ARRTrials] Already processing trial chain");
return;
}
isProcessing = true;
submarineManager.SetExternalPause(paused: true);
int startIndex = -1;
for (int i = Trials.Length - 1; i >= 0; i--)
{
if (!IsTrialComplete(Trials[i].TrialId))
{
for (int j = 0; j <= i; j++)
{
if (!IsTrialComplete(Trials[j].TrialId))
{
startIndex = j;
break;
}
}
break;
}
}
if (startIndex == -1)
{
log.Information("[ARRTrials] All trials already complete!");
isProcessing = false;
submarineManager.SetExternalPause(paused: false);
return;
}
currentTrialIndex = startIndex;
log.Information($"[ARRTrials] Starting from trial index {startIndex}: {Trials[startIndex].Name}");
ProcessCurrentTrial();
}
private void ProcessCurrentTrial()
{
if (currentTrialIndex < 0 || currentTrialIndex >= Trials.Length)
{
log.Information("[ARRTrials] Trial chain complete!");
isProcessing = false;
submarineManager.SetExternalPause(paused: false);
return;
}
var (questId, trialId, _, name) = Trials[currentTrialIndex];
if (IsTrialComplete(trialId))
{
log.Information("[ARRTrials] " + name + " already complete, moving to next");
currentTrialIndex++;
ProcessCurrentTrial();
}
else if (!QuestManager.IsQuestComplete(questId))
{
log.Information($"[ARRTrials] Queueing unlock quest {questId} for {name}");
questionableIPC.AddQuestPriority(questId.ToString());
framework.RunOnFrameworkThread(delegate
{
commandManager.ProcessCommand("/qst start");
});
waitingForQuest = true;
}
else
{
log.Information("[ARRTrials] " + name + " unlocked, inviting helper and triggering trial...");
helperManager.InviteHelpers();
waitingForParty = true;
}
}
public void OnQuestComplete(uint questId)
{
if (!isProcessing || !waitingForQuest)
{
return;
}
for (int i = currentTrialIndex; i < Trials.Length; i++)
{
if (Trials[i].QuestId == questId)
{
log.Information($"[ARRTrials] Unlock quest {questId} completed, triggering trial");
waitingForQuest = false;
helperManager.InviteHelpers();
waitingForParty = true;
break;
}
}
}
public void OnPartyReady()
{
if (isProcessing && waitingForParty)
{
waitingForParty = false;
TriggerCurrentTrial();
}
}
private void TriggerCurrentTrial()
{
if (currentTrialIndex >= 0 && currentTrialIndex < Trials.Length)
{
(uint, uint, string, string) tuple = Trials[currentTrialIndex];
string adCommand = tuple.Item3;
string name = tuple.Item4;
log.Information("[ARRTrials] Triggering " + name + " via AD command");
framework.RunOnFrameworkThread(delegate
{
chatGui.Print(new XivChatEntry
{
Message = "[QSTCompanion] Triggering " + name + "...",
Type = XivChatType.Echo
});
commandManager.ProcessCommand("/ad cfg Unsynced true");
commandManager.ProcessCommand(adCommand);
});
waitingForTrial = true;
}
}
public void OnDutyComplete()
{
if (!isProcessing || !waitingForTrial)
{
return;
}
(uint QuestId, uint TrialId, string ADCommand, string Name) tuple = Trials[currentTrialIndex];
uint trialId = tuple.TrialId;
string name = tuple.Name;
if (IsTrialComplete(trialId))
{
log.Information("[ARRTrials] " + name + " completed successfully!");
waitingForTrial = false;
currentTrialIndex++;
framework.RunOnFrameworkThread(delegate
{
ProcessCurrentTrial();
});
}
else
{
log.Warning("[ARRTrials] " + name + " NOT complete after verification. Retrying current step...");
waitingForTrial = false;
framework.RunOnFrameworkThread(delegate
{
ProcessCurrentTrial();
});
}
}
public string GetStatus()
{
if (!isProcessing)
{
return "Idle";
}
if (currentTrialIndex >= 0 && currentTrialIndex < Trials.Length)
{
string name = Trials[currentTrialIndex].Name;
if (waitingForQuest)
{
return "Waiting for " + name + " unlock quest";
}
if (waitingForParty)
{
return "Waiting for party (" + name + ")";
}
if (waitingForTrial)
{
return "In " + name;
}
return "Processing " + name;
}
return "Processing...";
}
public void Reset()
{
isProcessing = false;
currentTrialIndex = -1;
waitingForQuest = false;
waitingForParty = false;
waitingForTrial = false;
submarineManager.SetExternalPause(paused: false);
}
public void Dispose()
{
framework.Update -= OnFrameworkUpdate;
condition.ConditionChange -= OnConditionChanged;
log.Information("[ARRTrials] Service disposed");
}
private void OnConditionChanged(ConditionFlag flag, bool value)
{
if (flag == ConditionFlag.BoundByDuty)
{
if (value && !isInDuty)
{
isInDuty = true;
log.Debug("[ARRTrials] Entered duty");
}
else if (!value && isInDuty)
{
isInDuty = false;
OnDutyExited();
}
}
}
private void OnDutyExited()
{
if (!isProcessing || !waitingForTrial)
{
return;
}
log.Information("[ARRTrials] Exited duty - stopping AD and disbanding...");
framework.RunOnFrameworkThread(delegate
{
commandManager.ProcessCommand("/ad stop");
});
Task.Run(async delegate
{
await Task.Delay(2000);
framework.RunOnFrameworkThread(delegate
{
memoryHelper.SendChatMessage("/leave");
commandManager.ProcessCommand("/ad stop");
log.Information("[ARRTrials] /leave and safety /ad stop sent");
});
log.Information("[ARRTrials] Waiting for completion state check...");
await Task.Delay(1000);
(uint, uint, string, string) tuple = Trials[currentTrialIndex];
uint trialId = tuple.Item2;
for (int i = 0; i < 10; i++)
{
if (IsTrialComplete(trialId))
{
log.Information($"[ARRTrials] Completion verified on attempt {i + 1}");
break;
}
await Task.Delay(1000);
}
OnDutyComplete();
});
}
}