332 lines
11 KiB
C#
332 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Controller;
|
|
using Questionable.Data;
|
|
using Questionable.Functions;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Questing;
|
|
|
|
namespace Questionable.Windows.JournalComponents;
|
|
|
|
internal sealed class QuestJournalUtils
|
|
{
|
|
private readonly QuestController _questController;
|
|
|
|
private readonly FateController _fateController;
|
|
|
|
private readonly SeasonalDutyController _seasonalDutyController;
|
|
|
|
private readonly CustomDeliveryController _customDeliveryController;
|
|
|
|
private readonly AttunementController _attunementController;
|
|
|
|
private readonly QuestFunctions _questFunctions;
|
|
|
|
private readonly QuestData _questData;
|
|
|
|
private readonly QuestRegistry _questRegistry;
|
|
|
|
private readonly QuestChainComponent _questChainComponent;
|
|
|
|
private readonly IChatGui _chatGui;
|
|
|
|
private readonly ILogger<QuestJournalUtils> _logger;
|
|
|
|
private int _availableQuestCount;
|
|
|
|
private long _nextAvailableCountRefreshMs;
|
|
|
|
public Action? OpenJournal { get; set; }
|
|
|
|
public QuestJournalUtils(QuestController questController, FateController fateController, SeasonalDutyController seasonalDutyController, CustomDeliveryController customDeliveryController, AttunementController attunementController, QuestFunctions questFunctions, QuestData questData, QuestRegistry questRegistry, QuestChainComponent questChainComponent, IChatGui chatGui, ILogger<QuestJournalUtils> logger)
|
|
{
|
|
_questController = questController;
|
|
_fateController = fateController;
|
|
_seasonalDutyController = seasonalDutyController;
|
|
_customDeliveryController = customDeliveryController;
|
|
_attunementController = attunementController;
|
|
_questFunctions = questFunctions;
|
|
_questData = questData;
|
|
_questRegistry = questRegistry;
|
|
_questChainComponent = questChainComponent;
|
|
_chatGui = chatGui;
|
|
_logger = logger;
|
|
}
|
|
|
|
public void ShowContextMenu(IQuestInfo questInfo, Quest? quest, string label)
|
|
{
|
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
|
{
|
|
OpenContextMenu(questInfo);
|
|
}
|
|
DrawContextMenu(questInfo, quest, label);
|
|
}
|
|
|
|
public void OpenContextMenu(IQuestInfo questInfo)
|
|
{
|
|
ImU8String strId = new ImU8String(12, 1);
|
|
strId.AppendLiteral("##QuestPopup");
|
|
strId.AppendFormatted(questInfo.QuestId);
|
|
ImGui.OpenPopup(strId);
|
|
}
|
|
|
|
public bool DrawContextMenu(IQuestInfo questInfo, Quest? quest, string label)
|
|
{
|
|
ImU8String id = new ImU8String(12, 1);
|
|
id.AppendLiteral("##QuestPopup");
|
|
id.AppendFormatted(questInfo.QuestId);
|
|
using ImRaii.PopupDisposable popupDisposable = ImRaii.Popup(id);
|
|
if (!popupDisposable)
|
|
{
|
|
return false;
|
|
}
|
|
using (ImRaii.Disabled(!_questFunctions.IsReadyToAcceptQuest(questInfo.QuestId) || quest == null || quest.Root.Disabled || _fateController.IsRunning || _seasonalDutyController.IsRunning || _customDeliveryController.IsRunning || _attunementController.IsRunning))
|
|
{
|
|
if (ImGui.MenuItem("Start as next quest"))
|
|
{
|
|
StartQuestAsNext(quest, label);
|
|
}
|
|
}
|
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
{
|
|
if (!_questFunctions.IsReadyToAcceptQuest(questInfo.QuestId))
|
|
{
|
|
ImGui.SetTooltip("Quest is not ready to accept");
|
|
}
|
|
else if (quest == null)
|
|
{
|
|
ImGui.SetTooltip("Quest has no path available");
|
|
}
|
|
else if (quest.Root.Disabled)
|
|
{
|
|
ImGui.SetTooltip("Quest path is disabled");
|
|
}
|
|
}
|
|
List<ElementId> incompletePrerequisiteQuests = GetIncompletePrerequisiteQuests(questInfo);
|
|
using (ImRaii.Disabled(incompletePrerequisiteQuests.Count == 0))
|
|
{
|
|
if (ImGui.MenuItem(incompletePrerequisiteQuests.Count switch
|
|
{
|
|
0 => "Add quest and requirements to priority",
|
|
1 => "Add 1 quest to priority",
|
|
_ => $"Add {incompletePrerequisiteQuests.Count} quests to priority",
|
|
}))
|
|
{
|
|
AddRequiredQuestsToPriority(questInfo, incompletePrerequisiteQuests);
|
|
}
|
|
}
|
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
{
|
|
if (incompletePrerequisiteQuests.Count == 0)
|
|
{
|
|
ImGui.SetTooltip("No quests to add (quest may be complete, disabled, locked with no unlock path, or already in priority)");
|
|
}
|
|
else
|
|
{
|
|
ImGui.SetTooltip((incompletePrerequisiteQuests.Count == 1) ? "Add this quest to the priority list" : $"Add this quest and {incompletePrerequisiteQuests.Count - 1} required {((incompletePrerequisiteQuests.Count - 1 == 1) ? "quest" : "quests")} to the priority list in completion order");
|
|
}
|
|
}
|
|
if (label != "QuestChainComponent" && ImGui.MenuItem("View in Quest Chain"))
|
|
{
|
|
OpenJournal?.Invoke();
|
|
_questChainComponent.SelectQuest(questInfo.QuestId);
|
|
_questChainComponent.SelectTabAction?.Invoke("Quest Chain");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void StartQuestAsNext(Quest quest, string label)
|
|
{
|
|
string label2 = label + " start";
|
|
_fateController.Stop(label2);
|
|
_seasonalDutyController.Stop(label2);
|
|
_customDeliveryController.Stop(label2);
|
|
_attunementController.Stop(label2);
|
|
_questController.SetNextQuest(quest);
|
|
_questController.Start(label);
|
|
}
|
|
|
|
public unsafe List<ElementId> GetIncompletePrerequisiteQuests(IQuestInfo questInfo)
|
|
{
|
|
List<ElementId> list = new List<ElementId>();
|
|
HashSet<ElementId> visited = new HashSet<ElementId>();
|
|
CollectPrerequisitesRecursive(questInfo, list, visited);
|
|
list.Reverse();
|
|
Quest quest2;
|
|
List<ElementId> list2 = (from qId in list.Where(delegate(ElementId qId)
|
|
{
|
|
if (!_questData.TryGetQuestInfo(qId, out IQuestInfo questInfo2))
|
|
{
|
|
return false;
|
|
}
|
|
if (questInfo2.IsRepeatable && qId is QuestId questId2)
|
|
{
|
|
if (QuestManager.Instance()->IsDailyQuestCompleted(questId2.Value))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else if (_questFunctions.IsQuestComplete(qId))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
})
|
|
where !_questFunctions.IsQuestUnobtainable(qId)
|
|
where _questRegistry.IsKnownQuest(qId)
|
|
where (!_questRegistry.TryGetQuest(qId, out quest2) || !quest2.Root.Disabled) ? true : false
|
|
where !_questController.ManualPriorityQuests.Any((Quest q) => q.Id.Equals(qId))
|
|
select qId).ToList();
|
|
list2.Reverse();
|
|
bool flag = false;
|
|
flag = ((!questInfo.IsRepeatable || !(questInfo.QuestId is QuestId questId)) ? (!_questFunctions.IsQuestComplete(questInfo.QuestId)) : (!QuestManager.Instance()->IsDailyQuestCompleted(questId.Value)));
|
|
Quest quest;
|
|
bool flag2 = _questRegistry.TryGetQuest(questInfo.QuestId, out quest) && quest.Root.Disabled;
|
|
bool flag3 = _questFunctions.IsQuestLocked(questInfo.QuestId) && questInfo.PreviousQuests.Count == 0;
|
|
if (flag && !flag2 && !flag3 && !_questFunctions.IsQuestUnobtainable(questInfo.QuestId) && _questRegistry.IsKnownQuest(questInfo.QuestId) && !_questController.ManualPriorityQuests.Any((Quest q) => q.Id.Equals(questInfo.QuestId)))
|
|
{
|
|
list2.Add(questInfo.QuestId);
|
|
}
|
|
return list2;
|
|
}
|
|
|
|
private void CollectPrerequisitesRecursive(IQuestInfo questInfo, List<ElementId> prerequisites, HashSet<ElementId> visited)
|
|
{
|
|
if (visited.Contains(questInfo.QuestId))
|
|
{
|
|
return;
|
|
}
|
|
visited.Add(questInfo.QuestId);
|
|
if (questInfo.PreviousQuests.Count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
if (questInfo.PreviousQuestJoin == EQuestJoin.All)
|
|
{
|
|
foreach (PreviousQuestInfo previousQuest in questInfo.PreviousQuests)
|
|
{
|
|
if (_questData.TryGetQuestInfo(previousQuest.QuestId, out IQuestInfo questInfo2))
|
|
{
|
|
CollectPrerequisitesRecursive(questInfo2, prerequisites, visited);
|
|
if (!prerequisites.Contains(previousQuest.QuestId))
|
|
{
|
|
prerequisites.Add(previousQuest.QuestId);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (questInfo.PreviousQuestJoin != EQuestJoin.AtLeastOne)
|
|
{
|
|
return;
|
|
}
|
|
foreach (PreviousQuestInfo previousQuest2 in questInfo.PreviousQuests)
|
|
{
|
|
if (_questFunctions.IsQuestComplete(previousQuest2.QuestId))
|
|
{
|
|
continue;
|
|
}
|
|
if (_questData.TryGetQuestInfo(previousQuest2.QuestId, out IQuestInfo questInfo3))
|
|
{
|
|
CollectPrerequisitesRecursive(questInfo3, prerequisites, visited);
|
|
if (!prerequisites.Contains(previousQuest2.QuestId))
|
|
{
|
|
prerequisites.Add(previousQuest2.QuestId);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void AddRequiredQuestsToPriority(IQuestInfo questInfo, List<ElementId> prerequisiteQuests)
|
|
{
|
|
int num = 0;
|
|
foreach (ElementId prerequisiteQuest in prerequisiteQuests)
|
|
{
|
|
if (_questController.AddQuestPriority(prerequisiteQuest))
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
if (num > 0)
|
|
{
|
|
string text = ((num == 1) ? "quest" : "quests");
|
|
_logger.LogInformation("Added {Count} {QuestWord} for '{QuestName}' to priority list", num, text, questInfo.Name);
|
|
_chatGui.Print($"Added {num} {text} to priority list.", "Questionable", 576);
|
|
}
|
|
else
|
|
{
|
|
_logger.LogWarning("No quests were added to priority list for '{QuestName}'", questInfo.Name);
|
|
_chatGui.Print("No quests to add (may be complete, disabled, locked with no unlock path, or already in priority).", "Questionable", 576);
|
|
}
|
|
}
|
|
|
|
public void AddAllAvailableQuests()
|
|
{
|
|
if (Environment.TickCount64 >= _nextAvailableCountRefreshMs)
|
|
{
|
|
_nextAvailableCountRefreshMs = Environment.TickCount64 + 1000;
|
|
try
|
|
{
|
|
_availableQuestCount = _questRegistry.AllQuests.Count(delegate(Quest quest)
|
|
{
|
|
if (quest?.Info != null)
|
|
{
|
|
QuestRoot root = quest.Root;
|
|
if (root != null && !root.Disabled && !_questFunctions.IsQuestRemoved(quest.Id) && _questFunctions.IsReadyToAcceptQuest(quest.Id) && !_questFunctions.IsQuestComplete(quest.Id) && !_questFunctions.IsQuestAccepted(quest.Id) && !quest.Info.IsRepeatable && !(quest.Info is QuestInfo { MoogleDeliveryLevel: >0 }))
|
|
{
|
|
return !_questController.ManualPriorityQuests.Any((Quest q) => q.Id.Equals(quest.Id));
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
catch
|
|
{
|
|
_availableQuestCount = 0;
|
|
}
|
|
}
|
|
int availableQuestCount = _availableQuestCount;
|
|
using (ImRaii.Disabled(availableQuestCount == 0))
|
|
{
|
|
if (UiThemeUtils.IconButton(FontAwesomeIcon.ListOl, ImGui.GetFrameHeight()))
|
|
{
|
|
int num = _questController.AddAllAvailableQuests();
|
|
string value = ((num == 1) ? "quest" : "quests");
|
|
_chatGui.Print($"Added {num} {value} to priority queue.", "Questionable", 576);
|
|
_nextAvailableCountRefreshMs = 0L;
|
|
}
|
|
}
|
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
{
|
|
object obj2;
|
|
switch (availableQuestCount)
|
|
{
|
|
case 0:
|
|
ImGui.SetTooltip("No available quests to add");
|
|
return;
|
|
default:
|
|
obj2 = "quests";
|
|
break;
|
|
case 1:
|
|
obj2 = "quest";
|
|
break;
|
|
}
|
|
string value2 = (string)obj2;
|
|
ImU8String tooltip = new ImU8String(27, 2);
|
|
tooltip.AppendLiteral("Add ");
|
|
tooltip.AppendFormatted(availableQuestCount);
|
|
tooltip.AppendLiteral(" available ");
|
|
tooltip.AppendFormatted(value2);
|
|
tooltip.AppendLiteral(" to priority");
|
|
ImGui.SetTooltip(tooltip);
|
|
}
|
|
}
|
|
}
|