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.Interface.Windowing; using Dalamud.Plugin; using Dalamud.Plugin.Services; using LLib.ImGui; using Questionable.Controller; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; using Questionable.Windows.QuestComponents; using Questionable.Windows.Utils; namespace Questionable.Windows; internal sealed class PriorityWindow : LWindow { private const string ClipboardPrefix = "qst:priority:"; private const string LegacyClipboardPrefix = "qst:v1:"; private const char ClipboardSeparator = ';'; 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 PriorityWindow(QuestController questController, QuestFunctions questFunctions, QuestSelector questSelector, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IChatGui chatGui, IDalamudPluginInterface pluginInterface) : base("Quest Priority###QuestionableQuestPriority") { PriorityWindow priorityWindow = 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) { priorityWindow._questController.ManualPriorityQuests.Add(quest); }; base.Size = new Vector2(400f, 400f); base.SizeCondition = ImGuiCond.Once; base.SizeConstraints = new WindowSizeConstraints { MinimumSize = new Vector2(400f, 400f), MaximumSize = new Vector2(400f, 999f) }; } public override void DrawContent() { ImGui.Text("Quests to do first:"); _questSelector.DrawSelection(); DrawQuestList(); List list = ParseClipboardItems(); ImGui.BeginDisabled(list.Count == 0); if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Download, "Import from Clipboard")) { ImportFromClipboard(list); } ImGui.EndDisabled(); ImGui.SameLine(); ImGui.BeginDisabled(_questController.ManualPriorityQuests.Count == 0); if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Upload, "Export to Clipboard")) { ExportToClipboard(); } 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."); } ImGui.EndDisabled(); ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); ImGui.TextWrapped("If you have an active MSQ quest, Questionable will generally try to do:"); ImGui.BulletText("'Priority' quests: class quests, ARR primals, ARR raids"); ImGui.BulletText("Supported quests in your 'To-Do list'\n(quests from your Journal that are always on-screen)"); ImGui.BulletText("MSQ quest (if available, unless it is marked as 'ignored'\nin your Journal)"); ImGui.TextWrapped("If you don't have any active MSQ quest, it will always try to pick up the next quest in the MSQ first."); } private void DrawQuestList() { List manualPriorityQuests = _questController.ManualPriorityQuests; 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)) { (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); } } private static List ParseClipboardItems() { return DecodeQuestPriority(ImGui.GetClipboardText().Trim()); } public static List DecodeQuestPriority(string clipboardText) { List list = new List(); try { if (!string.IsNullOrEmpty(clipboardText)) { string text = null; if (clipboardText.StartsWith("qst:priority:", StringComparison.InvariantCulture)) { text = "qst:priority:"; } else if (clipboardText.StartsWith("qst:v1:", StringComparison.InvariantCulture)) { text = "qst:v1:"; } if (text != null) { clipboardText = clipboardText.Substring(text.Length); string[] array = Encoding.UTF8.GetString(Convert.FromBase64String(clipboardText)).Split(';'); for (int i = 0; i < array.Length; i++) { ElementId item = ElementId.FromString(array[i]); list.Add(item); } } } } catch (Exception) { list.Clear(); } return list; } public string EncodeQuestPriority() { return "qst:priority:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(';', _questController.ManualPriorityQuests.Select((Quest x) => x.Id.ToString())))); } private void ExportToClipboard() { ImGui.SetClipboardText(EncodeQuestPriority()); _chatGui.Print("Copied quests to clipboard.", "Questionable", 576); } private void ImportFromClipboard(List questElements) { _questController.ImportQuestPriority(questElements); } }