using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; using Dalamud.Bindings.ImGui; using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Components; using Dalamud.Interface.Utility.Raii; using Dalamud.Plugin; using Dalamud.Plugin.Services; using Questionable.Controller; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; using Questionable.Windows.Utils; namespace Questionable.Windows.QuestComponents; internal sealed class ManualPriorityComponent { private readonly QuestController _questController; private readonly QuestFunctions _questFunctions; private readonly QuestSelector _questSelector; private readonly QuestTooltipComponent _questTooltipComponent; private readonly UiUtils _uiUtils; private readonly IChatGui _chatGui; private readonly IDalamudPluginInterface _pluginInterface; private ElementId? _draggedItem; public ManualPriorityComponent(QuestController questController, QuestFunctions questFunctions, QuestSelector questSelector, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IChatGui chatGui, IDalamudPluginInterface pluginInterface) { ManualPriorityComponent manualPriorityComponent = this; _questController = questController; _questFunctions = questFunctions; _questSelector = questSelector; _questTooltipComponent = questTooltipComponent; _uiUtils = uiUtils; _chatGui = chatGui; _pluginInterface = pluginInterface; _questSelector.SuggestionPredicate = (Quest quest) => !quest.Info.IsMainScenarioQuest && !questFunctions.IsQuestUnobtainable(quest.Id) && questController.ManualPriorityQuests.All((Quest x) => x.Id != quest.Id); _questSelector.DefaultPredicate = (Quest quest) => questFunctions.IsQuestAccepted(quest.Id); _questSelector.QuestSelected = delegate(Quest quest) { manualPriorityComponent._questController.ManualPriorityQuests.Add(quest); }; } public void Draw() { ImGui.TextWrapped("When you have an active Main Scenario Quest, Questionable will prioritise quests in this order:"); ImGui.BulletText("Priority quests: class quests, A Realm Reborn primals and raids"); ImGui.BulletText("Supported quests from your Journal's To-Do list (always visible on-screen quests)"); ImGui.BulletText("Main Scenario Quest (if available and not marked as ignored in your Journal)"); ImGui.TextWrapped("Without an active Main Scenario Quest, it will always try to pick up the next MSQ first."); ImGui.Spacing(); ImGui.Text("Custom priority quests:"); _questSelector.DrawSelection(); ImGui.Spacing(); if (_questController.ManualPriorityQuests.Count > 0) { ImGui.Text("Priority queue (drag arrow buttons to reorder):"); } using (ImRaii.IEndObject endObject = ImRaii.Child("ManualPriorityList", new Vector2(-1f, -27f), border: true)) { if (endObject) { DrawQuestList(); } } List list = ParseClipboardItems(); using (ImRaii.Disabled(list.Count == 0)) { if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Download, "Import from Clipboard")) { ImportFromClipboard(list); } } ImGui.SameLine(); using (ImRaii.Disabled(_questController.ManualPriorityQuests.Count == 0)) { if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Upload, "Export to Clipboard")) { ExportToClipboard(); } ImGui.SameLine(); if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Remove finished Quests")) { _questController.ManualPriorityQuests.RemoveAll((Quest q) => _questFunctions.IsQuestComplete(q.Id)); } ImGui.SameLine(); using (ImRaii.Disabled(!ImGui.IsKeyDown(ImGuiKey.ModCtrl))) { if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Trash, "Clear All")) { _questController.ClearQuestPriority(); } } if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { ImGui.SetTooltip("Hold CTRL to enable this button."); } } } private void DrawQuestList() { List manualPriorityQuests = _questController.ManualPriorityQuests; if (manualPriorityQuests.Count == 0) { ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey); ImGui.TextWrapped("No quests in priority queue. Add quests using the selector above to prioritise them over other available quests."); ImGui.PopStyleColor(); return; } Quest quest = null; Quest quest2 = null; int index = 0; float x = ImGui.GetContentRegionAvail().X; List<(Vector2, Vector2)> list = new List<(Vector2, Vector2)>(); for (int i = 0; i < manualPriorityQuests.Count; i++) { Vector2 item = ImGui.GetCursorScreenPos() + new Vector2(0f, (0f - ImGui.GetStyle().ItemSpacing.Y) / 2f); Quest quest3 = manualPriorityQuests[i]; ImU8String id = new ImU8String(5, 1); id.AppendLiteral("Quest"); id.AppendFormatted(quest3.Id); using (ImRaii.PushId(id)) { ImGui.AlignTextToFramePadding(); id = new ImU8String(1, 1); id.AppendFormatted(i + 1); id.AppendLiteral("."); ImGui.Text(id); ImGui.SameLine(); (Vector4, FontAwesomeIcon, string) questStyle = _uiUtils.GetQuestStyle(quest3.Id); bool flag; using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) { ImGui.AlignTextToFramePadding(); ImGui.TextColored(in questStyle.Item1, questStyle.Item2.ToIconString()); flag = ImGui.IsItemHovered(); } ImGui.SameLine(); ImGui.AlignTextToFramePadding(); ImGui.Text(quest3.Info.Name); flag |= ImGui.IsItemHovered(); if (flag) { _questTooltipComponent.Draw(quest3.Info); } if (manualPriorityQuests.Count > 1) { using (ImRaii.PushFont(UiBuilder.IconFont)) { ImGui.SameLine(ImGui.GetContentRegionAvail().X + ImGui.GetStyle().WindowPadding.X - ImGui.CalcTextSize(FontAwesomeIcon.ArrowsUpDown.ToIconString()).X - ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X - ImGui.GetStyle().FramePadding.X * 4f - ImGui.GetStyle().ItemSpacing.X); } if (_draggedItem == quest3.Id) { ImGuiComponents.IconButton("##Move", FontAwesomeIcon.ArrowsUpDown, ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.ButtonActive))); } else { ImGuiComponents.IconButton("##Move", FontAwesomeIcon.ArrowsUpDown); } if (_draggedItem == null && ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left)) { _draggedItem = quest3.Id; } ImGui.SameLine(); } else { using (ImRaii.PushFont(UiBuilder.IconFont)) { ImGui.SameLine(ImGui.GetContentRegionAvail().X + ImGui.GetStyle().WindowPadding.X - ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X - ImGui.GetStyle().FramePadding.X * 2f); } } if (ImGuiComponents.IconButton($"##Remove{i}", FontAwesomeIcon.Times)) { quest = quest3; } } Vector2 item2 = new Vector2(item.X + x, ImGui.GetCursorScreenPos().Y - ImGui.GetStyle().ItemSpacing.Y + 2f); list.Add((item, item2)); } if (!ImGui.IsMouseDragging(ImGuiMouseButton.Left)) { _draggedItem = null; } else if (_draggedItem != null) { Quest item3 = manualPriorityQuests.Single((Quest quest4) => quest4.Id == _draggedItem); int num = manualPriorityQuests.IndexOf(item3); var (pMin, pMax) = list[num]; ImGui.GetWindowDrawList().AddRect(pMin, pMax, ImGui.GetColorU32(ImGuiColors.DalamudGrey), 3f, ImDrawFlags.RoundCornersAll); int num2 = list.FindIndex(((Vector2 TopLeft, Vector2 BottomRight) tuple2) => ImGui.IsMouseHoveringRect(tuple2.TopLeft, tuple2.BottomRight, clip: true)); if (num2 >= 0 && num != num2) { quest2 = manualPriorityQuests.Single((Quest quest4) => quest4.Id == _draggedItem); index = num2; } } if (quest != null) { manualPriorityQuests.Remove(quest); } if (quest2 != null) { manualPriorityQuests.Remove(quest2); manualPriorityQuests.Insert(index, quest2); } } public string EncodeQuestPriority() { return "qst:priority:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(';', _questController.ManualPriorityQuests.Select((Quest x) => x.Id.ToString())))); } private static List ParseClipboardItems() { return PriorityWindow.DecodeQuestPriority(ImGui.GetClipboardText().Trim()); } private void ExportToClipboard() { ImGui.SetClipboardText(EncodeQuestPriority()); _chatGui.Print("Copied quests to clipboard.", "Questionable", 576); } private void ImportFromClipboard(List questElements) { _questController.ImportQuestPriority(questElements); } }