using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text.RegularExpressions; using System.Text.RegularExpressions.Generated; using Dalamud.Bindings.ImGui; using Dalamud.Game.Text; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin.Services; using Microsoft.Extensions.Logging; using Questionable.Controller; using Questionable.Controller.Steps.Shared; using Questionable.External; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; namespace Questionable.Windows.QuestComponents; internal sealed class ActiveQuestComponent { private readonly QuestController _questController; private readonly MovementController _movementController; private readonly CombatController _combatController; private readonly GatheringController _gatheringController; private readonly QuestFunctions _questFunctions; private readonly ICommandManager _commandManager; private readonly Configuration _configuration; private readonly QuestRegistry _questRegistry; private readonly PriorityWindow _priorityWindow; private readonly UiUtils _uiUtils; private readonly IObjectTable _objectTable; private readonly IClientState _clientState; private readonly IChatGui _chatGui; private readonly AutoDutyIpc _autoDutyIpc; private readonly ILogger _logger; public event EventHandler? Reload; [GeneratedRegex("\\s\\s+", RegexOptions.IgnoreCase, "en-US")] [GeneratedCode("System.Text.RegularExpressions.Generator", "10.0.13.7005")] private static Regex MultipleWhitespaceRegex() { return _003CRegexGenerator_g_003EFBB8301322196CF81C64F1652C2FA6E1D6BF3907141F781E9D97ABED51BF056C4__MultipleWhitespaceRegex_0.Instance; } public ActiveQuestComponent(QuestController questController, MovementController movementController, CombatController combatController, GatheringController gatheringController, QuestFunctions questFunctions, ICommandManager commandManager, Configuration configuration, QuestRegistry questRegistry, PriorityWindow priorityWindow, UiUtils uiUtils, IObjectTable objectTable, IClientState clientState, IChatGui chatGui, AutoDutyIpc autoDutyIpc, ILogger logger) { _questController = questController; _movementController = movementController; _combatController = combatController; _gatheringController = gatheringController; _questFunctions = questFunctions; _commandManager = commandManager; _configuration = configuration; _questRegistry = questRegistry; _priorityWindow = priorityWindow; _uiUtils = uiUtils; _objectTable = objectTable; _clientState = clientState; _chatGui = chatGui; _autoDutyIpc = autoDutyIpc; _logger = logger; } public void Draw(bool isMinimized) { (QuestController.QuestProgress, QuestController.ECurrentQuestType)? currentQuestDetails = _questController.CurrentQuestDetails; QuestController.QuestProgress questProgress = currentQuestDetails?.Item1; QuestController.ECurrentQuestType? currentQuestType = currentQuestDetails?.Item2; if (questProgress != null) { DrawQuestNames(questProgress, currentQuestType); QuestProgressInfo questProgressInfo = DrawQuestWork(questProgress, isMinimized); if (_combatController.IsRunning) { ImGui.TextColored(ImGuiColors.DalamudOrange, "In Combat"); } else { string currentTaskState = _questController.CurrentTaskState; if (currentTaskState != null) { using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudOrange)) { ImGui.TextUnformatted(currentTaskState); } } else { using (ImRaii.Disabled()) { ImGui.TextUnformatted(_questController.DebugState ?? string.Empty); } } } try { QuestSequence questSequence = questProgress.Quest.FindSequence(questProgress.Sequence); QuestStep questStep = questSequence?.FindStep(questProgress.Step); if (!isMinimized) { using (ImRaii.Color color = new ImRaii.Color()) { bool flag; if (questStep != null) { EInteractionType interactionType = questStep.InteractionType; if (interactionType == EInteractionType.WaitForManualProgress || interactionType == EInteractionType.Snipe || interactionType == EInteractionType.Instruction) { flag = true; goto IL_0154; } } flag = false; goto IL_0154; IL_0154: if (flag) { color.Push(ImGuiCol.Text, ImGuiColors.DalamudOrange); } ImGui.TextUnformatted(questStep?.Comment ?? questSequence?.Comment ?? questProgress.Quest.Root.Comment ?? string.Empty); } ImGui.Text(_questController.ToStatString()); } DrawQuestButtons(questProgress, questStep, questProgressInfo, isMinimized); } catch (Exception ex) { ImGui.TextColored(ImGuiColors.DalamudRed, ex.ToString()); _logger.LogError(ex, "Could not handle active quest buttons"); } DrawSimulationControls(); return; } (bool isLevelLocked, int levelsNeeded, int requiredLevel, string? questName) msqLevelLockInfo = _questFunctions.GetMsqLevelLockInfo(); bool item = msqLevelLockInfo.isLevelLocked; int item2 = msqLevelLockInfo.levelsNeeded; int item3 = msqLevelLockInfo.requiredLevel; string item4 = msqLevelLockInfo.questName; int currentPlayerLevel = _objectTable.LocalPlayer?.Level ?? 0; if (item && _autoDutyIpc.IsConfiguredToRunLevelingMode(currentPlayerLevel) && item4 != null) { Vector4 col = ImGuiColors.DalamudYellow; ImU8String text = new ImU8String(22, 2); text.AppendLiteral("MSQ '"); text.AppendFormatted(item4); text.AppendLiteral("' requires level "); text.AppendFormatted(item3); ImGui.TextColored(in col, text); col = ImGuiColors.DalamudGrey; ImU8String text2 = new ImU8String(61, 2); text2.AppendLiteral("You need "); text2.AppendFormatted(item2); text2.AppendLiteral(" more level"); text2.AppendFormatted((item2 == 1) ? string.Empty : "s"); text2.AppendLiteral(" - Leveling mode will start automatically"); ImGui.TextColored(in col, text2); using (ImRaii.Disabled(_questController.IsRunning || !_autoDutyIpc.IsStopped())) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { _logger.LogInformation("Start button clicked with MSQ level-locked. Starting AutoDuty leveling mode."); if (_autoDutyIpc.StartLevelingMode()) { _chatGui.Print($"Starting AutoDuty Leveling mode to reach level {item3} for MSQ '{item4}'.", "Questionable", 576); } else { _chatGui.PrintError("Failed to start AutoDuty Leveling mode. Please check that AutoDuty is installed and configured.", "Questionable", 576); } } } if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { if (!_autoDutyIpc.IsStopped()) { ImGui.SetTooltip("AutoDuty is currently running."); } else { ImU8String tooltip = new ImU8String(53, 1); tooltip.AppendLiteral("Start AutoDuty Leveling mode to reach level "); tooltip.AppendFormatted(item3); tooltip.AppendLiteral(" for MSQ."); ImGui.SetTooltip(tooltip); } } } else { ImGui.Text("No active quest"); } if (!isMinimized) { Vector4 col = ImGuiColors.DalamudGrey; ImU8String text3 = new ImU8String(14, 1); text3.AppendFormatted(_questRegistry.Count); text3.AppendLiteral(" quests loaded"); ImGui.TextColored(in col, text3); } ImGui.SameLine(); if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop)) { _movementController.Stop(); _questController.Stop("Manual (no active quest)"); _gatheringController.Stop("Manual (no active quest)"); if (!_autoDutyIpc.IsStopped()) { try { _autoDutyIpc.Stop(); } catch (Exception exception) { _logger.LogWarning(exception, "Failed to stop AutoDuty"); } } } ImGui.SameLine(); if (ImGuiComponents.IconButton(FontAwesomeIcon.SortAmountDown)) { _priorityWindow.ToggleOrUncollapse(); } } private void DrawQuestNames(QuestController.QuestProgress currentQuest, QuestController.ECurrentQuestType? currentQuestType) { if (currentQuestType == QuestController.ECurrentQuestType.Simulated) { using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed)) { ImU8String text = new ImU8String(26, 4); text.AppendLiteral("Simulated Quest: "); text.AppendFormatted(Shorten(currentQuest.Quest.Info.Name)); text.AppendLiteral(" ("); text.AppendFormatted(currentQuest.Quest.Id); text.AppendLiteral(") / "); text.AppendFormatted(currentQuest.Sequence); text.AppendLiteral(" / "); text.AppendFormatted(currentQuest.Step); ImGui.TextUnformatted(text); return; } } if (currentQuestType == QuestController.ECurrentQuestType.Gathering) { using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.ParsedGold)) { ImU8String text2 = new ImU8String(20, 4); text2.AppendLiteral("Gathering: "); text2.AppendFormatted(Shorten(currentQuest.Quest.Info.Name)); text2.AppendLiteral(" ("); text2.AppendFormatted(currentQuest.Quest.Id); text2.AppendLiteral(") / "); text2.AppendFormatted(currentQuest.Sequence); text2.AppendLiteral(" / "); text2.AppendFormatted(currentQuest.Step); ImGui.TextUnformatted(text2); return; } } QuestController.QuestProgress startedQuest = _questController.StartedQuest; if (startedQuest != null) { if (startedQuest.Quest.Source == Quest.ESource.UserDirectory) { ImGui.PushFont(UiBuilder.IconFont); ImGui.TextColored(ImGuiColors.DalamudOrange, FontAwesomeIcon.FilePen.ToIconString()); ImGui.PopFont(); ImGui.SameLine(0f); if (ImGui.IsItemHovered()) { ImGui.SetTooltip("This quest is loaded from your 'pluginConfigs\\Questionable\\Quests' directory.\nThis gets loaded even if Questionable ships with a newer/different version of the quest."); } } ImU8String text3 = new ImU8String(16, 4); text3.AppendLiteral("Quest: "); text3.AppendFormatted(Shorten(startedQuest.Quest.Info.Name)); text3.AppendLiteral(" ("); text3.AppendFormatted(startedQuest.Quest.Id); text3.AppendLiteral(") / "); text3.AppendFormatted(startedQuest.Sequence); text3.AppendLiteral(" / "); text3.AppendFormatted(startedQuest.Step); ImGui.TextUnformatted(text3); if (startedQuest.Quest.Root.Disabled) { ImGui.SameLine(); ImGui.TextColored(ImGuiColors.DalamudRed, "Disabled"); } bool flag = _configuration.Stop.Enabled && _configuration.Stop.LevelStopMode != Configuration.EStopConditionMode.Off; bool flag2 = _configuration.Stop.Enabled && _configuration.Stop.QuestsToStopAfter.Any((ElementId x) => !_questFunctions.IsQuestComplete(x) && !_questFunctions.IsQuestUnobtainable(x)); bool flag3 = _configuration.Stop.Enabled && _configuration.Stop.SequenceStopMode != Configuration.EStopConditionMode.Off; if (flag || flag2 || flag3) { ImGui.SameLine(); Vector4 col = ImGuiColors.ParsedPurple; if (flag) { int num = _objectTable.LocalPlayer?.Level ?? 0; if (num > 0 && num >= _configuration.Stop.TargetLevel) { col = ImGuiColors.ParsedGreen; } else if (num > 0) { col = ImGuiColors.ParsedBlue; } } if (flag3) { col = ((startedQuest.Sequence < _configuration.Stop.TargetSequence) ? ImGuiColors.ParsedBlue : ImGuiColors.ParsedGreen); } ImGui.TextColored(in col, SeIconChar.Clock.ToIconString()); if (ImGui.IsItemHovered()) { using ImRaii.IEndObject endObject = ImRaii.Tooltip(); if (endObject) { ImGui.Text("Stop Conditions:"); ImGui.Separator(); if (flag) { int num2 = _objectTable.LocalPlayer?.Level ?? 0; ImU8String text4 = new ImU8String(14, 1); text4.AppendLiteral("Stop at level "); text4.AppendFormatted(_configuration.Stop.TargetLevel); ImGui.BulletText(text4); if (num2 > 0) { ImGui.SameLine(); if (num2 >= _configuration.Stop.TargetLevel) { Vector4 col2 = ImGuiColors.ParsedGreen; ImU8String text5 = new ImU8String(22, 1); text5.AppendLiteral("(Current: "); text5.AppendFormatted(num2); text5.AppendLiteral(" - Reached!)"); ImGui.TextColored(in col2, text5); } else { Vector4 col2 = ImGuiColors.ParsedBlue; ImU8String text6 = new ImU8String(11, 1); text6.AppendLiteral("(Current: "); text6.AppendFormatted(num2); text6.AppendLiteral(")"); ImGui.TextColored(in col2, text6); } } } if (flag3) { if (flag) { ImGui.Spacing(); } int sequence = startedQuest.Sequence; ImU8String text7 = new ImU8String(23, 1); text7.AppendLiteral("Stop at quest sequence "); text7.AppendFormatted(_configuration.Stop.TargetSequence); ImGui.BulletText(text7); ImGui.SameLine(); if (sequence >= _configuration.Stop.TargetSequence) { Vector4 col2 = ImGuiColors.ParsedGreen; ImU8String text8 = new ImU8String(22, 1); text8.AppendLiteral("(Current: "); text8.AppendFormatted(sequence); text8.AppendLiteral(" - Reached!)"); ImGui.TextColored(in col2, text8); } else { Vector4 col2 = ImGuiColors.ParsedBlue; ImU8String text9 = new ImU8String(11, 1); text9.AppendLiteral("(Current: "); text9.AppendFormatted(sequence); text9.AppendLiteral(")"); ImGui.TextColored(in col2, text9); } } if (flag2) { if (flag || flag3) { ImGui.Spacing(); } ImGui.BulletText("Stop after completing any of these quests:"); ImGui.Indent(); foreach (ElementId item in _configuration.Stop.QuestsToStopAfter) { if (_questRegistry.TryGetQuest(item, out Quest quest)) { var (color, icon, _) = _uiUtils.GetQuestStyle(item); _uiUtils.ChecklistItem($"{quest.Info.Name} ({item})", color, icon); } } ImGui.Unindent(); } } } } if (_configuration.Advanced.AdditionalStatusInformation && _questController.IsInterruptible()) { ImGui.SameLine(); ImGui.TextColored(ImGuiColors.DalamudYellow, SeIconChar.Hyadelyn.ToIconString()); if (ImGui.IsItemHovered()) { using ImRaii.IEndObject endObject2 = ImRaii.Tooltip(); if (endObject2) { ImGui.Text("This quest sequence starts with a teleport to an Aetheryte."); ImGui.Text("Certain priority quest (e.g. class quests) may be started/completed by the plugin prior to continuing with this quest."); ImGui.Separator(); ImGui.Text("Available priority quests:"); List nextPriorityQuestsThatCanBeAccepted = _questFunctions.GetNextPriorityQuestsThatCanBeAccepted(); List list = (from x in nextPriorityQuestsThatCanBeAccepted where x.IsAvailable select x.QuestId).ToList(); if (list.Count > 0) { foreach (ElementId item2 in list) { if (_questRegistry.TryGetQuest(item2, out Quest quest2)) { ImU8String text10 = new ImU8String(3, 2); text10.AppendFormatted(quest2.Info.Name); text10.AppendLiteral(" ("); text10.AppendFormatted(item2); text10.AppendLiteral(")"); ImGui.BulletText(text10); } } } else { ImGui.BulletText("(none)"); } if (_configuration.Advanced.AdditionalStatusInformation) { List list2 = nextPriorityQuestsThatCanBeAccepted.Where((PriorityQuestInfo x) => !x.IsAvailable).ToList(); if (list2.Count > 0) { ImGui.Text("Unavailable priority quests:"); foreach (var (elementId2, value) in list2) { if (_questRegistry.TryGetQuest(elementId2, out Quest quest3)) { ImU8String text12 = new ImU8String(6, 3); text12.AppendFormatted(quest3.Info.Name); text12.AppendLiteral(" ("); text12.AppendFormatted(elementId2); text12.AppendLiteral(") - "); text12.AppendFormatted(value); ImGui.BulletText(text12); } } } } } } } } QuestController.QuestProgress nextQuest = _questController.NextQuest; if (nextQuest != null) { using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow)) { ImU8String text13 = new ImU8String(21, 4); text13.AppendLiteral("Next Quest: "); text13.AppendFormatted(Shorten(nextQuest.Quest.Info.Name)); text13.AppendLiteral(" ("); text13.AppendFormatted(nextQuest.Quest.Id); text13.AppendLiteral(") / "); text13.AppendFormatted(nextQuest.Sequence); text13.AppendLiteral(" / "); text13.AppendFormatted(nextQuest.Step); ImGui.TextUnformatted(text13); } } } private unsafe QuestProgressInfo? DrawQuestWork(QuestController.QuestProgress currentQuest, bool isMinimized) { QuestProgressInfo questProgressInfo = _questFunctions.GetQuestProgressInfo(currentQuest.Quest.Id); if (questProgressInfo != null) { if (isMinimized) { return questProgressInfo; } Vector4* styleColorVec = ImGui.GetStyleColorVec4(ImGuiCol.TextDisabled); Vector4 color = ((styleColorVec == null) ? ImGuiColors.ParsedOrange : (*styleColorVec)); using (ImRaii.PushColor(ImGuiCol.Text, color)) { ImU8String text = new ImU8String(0, 1); text.AppendFormatted(questProgressInfo); ImGui.Text(text); if (ImGui.IsItemClicked()) { string text2 = MultipleWhitespaceRegex().Replace(questProgressInfo.ToString(), " "); ImGui.SetClipboardText(text2); _chatGui.Print("Copied '" + text2 + "' to clipboard"); } if (ImGui.IsItemHovered()) { ImGui.SameLine(); ImGui.PushFont(UiBuilder.IconFont); ImGui.Text(FontAwesomeIcon.Copy.ToIconString()); ImGui.PopFont(); } if (currentQuest.Quest.Info.AlliedSociety != EAlliedSociety.None) { ImGui.SameLine(); ImU8String text3 = new ImU8String(2, 1); text3.AppendLiteral("/ "); text3.AppendFormatted(questProgressInfo.ClassJob); ImGui.Text(text3); } } } else if (currentQuest.Quest.Id is QuestId) { using (ImRaii.Disabled()) { if (currentQuest.Quest.Id == _questController.NextQuest?.Quest.Id) { ImGui.TextUnformatted("(Next quest in story line not accepted)"); } else { ImGui.TextUnformatted("(Not accepted)"); } } } return questProgressInfo; } private void DrawQuestButtons(QuestController.QuestProgress currentQuest, QuestStep? currentStep, QuestProgressInfo? questProgressInfo, bool isMinimized) { using (ImRaii.Disabled(_questController.IsRunning)) { if (ImGuiComponents.IconButton(FontAwesomeIcon.Play)) { if (questProgressInfo == null) { _questController.SetNextQuest(currentQuest.Quest); } _questController.Start("UI start"); } if (!isMinimized) { ImGui.SameLine(); if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.StepForward, "Step")) { _questController.StartSingleStep("UI step"); } } } ImGui.SameLine(); if (ImGuiComponents.IconButton(FontAwesomeIcon.Stop)) { _movementController.Stop(); _questController.Stop("UI stop"); _gatheringController.Stop("UI stop"); } if (isMinimized) { ImGui.SameLine(); if (ImGuiComponents.IconButton(FontAwesomeIcon.RedoAlt)) { this.Reload?.Invoke(this, EventArgs.Empty); } return; } bool flag = currentStep == currentQuest.Quest.FindSequence(currentQuest.Sequence)?.Steps.LastOrDefault(); WaitAtEnd.WaitNextStepOrSequence task; bool condition = currentStep != null && !flag && currentStep.InteractionType == EInteractionType.Instruction && _questController.HasCurrentTaskMatching(out task); using (ImRaii.Disabled(flag)) { using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.ParsedGreen, condition)) { if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.ArrowCircleRight, "Skip")) { _movementController.Stop(); _questController.Skip(currentQuest.Quest.Id, currentQuest.Sequence); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Skip the current step of the quest path."); } } } if (_commandManager.Commands.ContainsKey("/questinfo")) { ImGui.SameLine(); if (ImGuiComponents.IconButton(FontAwesomeIcon.Atlas)) { _commandManager.ProcessCommand($"/questinfo {currentQuest.Quest.Id}"); } if (ImGui.IsItemHovered()) { ImU8String tooltip = new ImU8String(46, 1); tooltip.AppendLiteral("Show information about '"); tooltip.AppendFormatted(currentQuest.Quest.Info.Name); tooltip.AppendLiteral("' in Quest Map plugin."); ImGui.SetTooltip(tooltip); } } } private void DrawSimulationControls() { if (_questController.SimulatedQuest == null) { return; } QuestController.QuestProgress simulatedQuest = _questController.SimulatedQuest; ImGui.Separator(); ImGui.TextColored(ImGuiColors.DalamudRed, "Quest sim active (experimental)"); ImU8String text = new ImU8String(10, 1); text.AppendLiteral("Sequence: "); text.AppendFormatted(simulatedQuest.Sequence); ImGui.Text(text); ImGui.BeginDisabled(simulatedQuest.Sequence == 0); if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus)) { _movementController.Stop(); _questController.Stop("Sim-"); byte oldSequence = simulatedQuest.Sequence; byte sequence = simulatedQuest.Quest.Root.QuestSequence.Select((QuestSequence x) => x.Sequence).LastOrDefault((byte x) => x < oldSequence, 0); _questController.SimulatedQuest.SetSequence(sequence); } ImGui.EndDisabled(); ImGui.SameLine(); ImGui.BeginDisabled(simulatedQuest.Sequence >= byte.MaxValue); if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) { _movementController.Stop(); _questController.Stop("Sim+"); byte oldSequence2 = simulatedQuest.Sequence; byte sequence2 = simulatedQuest.Quest.Root.QuestSequence.Select((QuestSequence x) => x.Sequence).FirstOrDefault((byte x) => x > oldSequence2, byte.MaxValue); simulatedQuest.SetSequence(sequence2); } ImGui.EndDisabled(); QuestSequence questSequence = simulatedQuest.Quest.FindSequence(simulatedQuest.Sequence); if (questSequence == null) { return; } using (ImRaii.PushId("SimulatedStep")) { ImU8String text2 = new ImU8String(9, 2); text2.AppendLiteral("Step: "); text2.AppendFormatted(simulatedQuest.Step); text2.AppendLiteral(" / "); text2.AppendFormatted(questSequence.Steps.Count - 1); ImGui.Text(text2); ImGui.BeginDisabled(simulatedQuest.Step == 0); if (ImGuiComponents.IconButton(FontAwesomeIcon.Minus)) { _movementController.Stop(); _questController.Stop("SimStep-"); simulatedQuest.SetStep(Math.Min(simulatedQuest.Step - 1, questSequence.Steps.Count - 1)); } ImGui.EndDisabled(); ImGui.SameLine(); ImGui.BeginDisabled(simulatedQuest.Step >= questSequence.Steps.Count); if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus)) { _movementController.Stop(); _questController.Stop("SimStep+"); simulatedQuest.SetStep((simulatedQuest.Step == questSequence.Steps.Count - 1) ? 255 : (simulatedQuest.Step + 1)); } ImGui.EndDisabled(); if (ImGui.Button("Skip current task")) { _questController.SkipSimulatedTask(); } ImGui.SameLine(); if (ImGui.Button("Clear sim")) { _questController.SimulateQuest(null, 0, 0); _movementController.Stop(); _questController.Stop("ClearSim"); } } } private static string Shorten(string text) { if (text.Length > 35) { return string.Concat(text.AsSpan(0, 30).Trim(), ((SeIconChar)57434).ToIconString().AsSpan()); } return text; } }