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.Utility.Raii; using Dalamud.Plugin; using Microsoft.Extensions.Logging; using Questionable.Controller; using Questionable.Functions; using Questionable.Model; using Questionable.Model.Questing; using Questionable.Windows.QuestComponents; using Questionable.Windows.Utils; namespace Questionable.Windows.ConfigComponents; internal sealed class BlacklistConfigComponent : ConfigComponent { private const string ClipboardPrefix = "qst:blacklist:"; private readonly IDalamudPluginInterface _pluginInterface; private readonly QuestSelector _questSelector; private readonly QuestRegistry _questRegistry; private readonly QuestFunctions _questFunctions; private readonly QuestTooltipComponent _questTooltipComponent; private readonly UiUtils _uiUtils; private readonly ILogger _logger; private List _acceptedQuestsCache = new List(); private long _nextAcceptedQuestsRefreshMs; public BlacklistConfigComponent(IDalamudPluginInterface pluginInterface, QuestSelector questSelector, QuestFunctions questFunctions, QuestRegistry questRegistry, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, Configuration configuration, ILogger logger) : base(pluginInterface, configuration) { BlacklistConfigComponent blacklistConfigComponent = this; _pluginInterface = pluginInterface; _questSelector = questSelector; _questRegistry = questRegistry; _questFunctions = questFunctions; _questTooltipComponent = questTooltipComponent; _uiUtils = uiUtils; _logger = logger; _questSelector.SuggestionPredicate = (Quest quest) => !configuration.General.BlacklistedQuests.Contains(quest.Id); _questSelector.DefaultPredicate = (Quest quest) => !quest.Root.Disabled && questFunctions.IsQuestAccepted(quest.Id); _questSelector.QuestSelected = delegate(Quest quest) { configuration.General.BlacklistedQuests.Add(quest.Id); blacklistConfigComponent.Save(); }; } public override void DrawTab() { UiThemeUtils.SectionHeader("Quest Blacklist"); var (contentStartPos, availableWidth, drawList) = UiThemeUtils.BeginCard(); ImGui.TextWrapped("Blacklisted quests will never be automatically picked up or executed by Questionable."); ImGui.TextWrapped("Use this to permanently skip specific quests you don't want to do."); DrawCurrentlyAcceptedQuests(); HashSet blacklistedQuests = base.Configuration.General.BlacklistedQuests; ImU8String text = new ImU8String(22, 1); text.AppendLiteral("Blacklisted Quests ("); text.AppendFormatted(blacklistedQuests.Count); text.AppendLiteral("):"); ImGui.Text(text); using (ImRaii.ChildDisposable childDisposable = ImRaii.Child("BlacklistedQuestsList", new Vector2(-1f, 200f), border: true)) { if ((bool)childDisposable) { if (blacklistedQuests.Count > 0) { ElementId elementId = null; using ImRaii.TableDisposable tableDisposable = UiThemeUtils.BeginThemedTable("BlacklistedQuestsTable", 2); if ((bool)tableDisposable) { ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch); ImGui.TableSetupColumn("##Action", ImGuiTableColumnFlags.WidthFixed, 40f * ImGui.GetIO().FontGlobalScale); foreach (ElementId item in blacklistedQuests.OrderBy((ElementId x) => x.ToString())) { ImGui.TableNextRow(); ImU8String id = new ImU8String(5, 1); id.AppendLiteral("Quest"); id.AppendFormatted(item); using (ImRaii.PushId(id)) { if (!_questRegistry.TryGetQuest(item, out Quest quest)) { if (ImGui.TableNextColumn()) { ImGui.AlignTextToFramePadding(); ImU8String text2 = new ImU8String(16, 1); text2.AppendLiteral("Unknown Quest ("); text2.AppendFormatted(item); text2.AppendLiteral(")"); ImGui.TextColored(in UiThemeUtils.DimTextColor, text2); } if (ImGui.TableNextColumn() && UiThemeUtils.IconButton(FontAwesomeIcon.Times, ImGui.GetFrameHeight())) { elementId = item; } continue; } (Vector4, FontAwesomeIcon, string) questStyle = _uiUtils.GetQuestStyle(item); if (ImGui.TableNextColumn()) { bool flag; using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) { ImGui.AlignTextToFramePadding(); ImGui.TextColored(in questStyle.Item1, questStyle.Item2.ToIconString()); flag = ImGui.IsItemHovered(); } ImGui.SameLine(); ImGui.AlignTextToFramePadding(); UiThemeUtils.RowLabel(quest.Info.Name, 0f); flag |= ImGui.IsItemHovered(); if (flag) { _questTooltipComponent.Draw(quest.Info); } } if (ImGui.TableNextColumn()) { if (UiThemeUtils.IconButton(FontAwesomeIcon.Times, ImGui.GetFrameHeight())) { elementId = item; } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Remove from blacklist"); } } } } } if (elementId != null) { base.Configuration.General.BlacklistedQuests.Remove(elementId); Save(); } } else { UiThemeUtils.EmptyState(FontAwesomeIcon.Ban, "No quests blacklisted", "Use the dropdown above or add from currently accepted quests"); } } } _questSelector.DrawSelection(); DrawClipboardButtons(blacklistedQuests); if (blacklistedQuests.Count > 0) { ImGui.SameLine(); if (UiThemeUtils.DestructiveButton(FontAwesomeIcon.Trash, "Clear all")) { base.Configuration.General.BlacklistedQuests.Clear(); Save(); } } UiThemeUtils.EndCard(contentStartPos, availableWidth, drawList); } private void DrawClipboardButtons(HashSet blacklistedQuests) { using (ImRaii.Disabled(blacklistedQuests.Count == 0)) { if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Copy, "Copy")) { string s = string.Join(";", from x in blacklistedQuests orderby x.ToString() select x.ToString()); ImGui.SetClipboardText("qst:blacklist:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s))); } } ImGui.SameLine(); string text = ThrottledClipboard.GetText(); using (ImRaii.Disabled(!text.StartsWith("qst:blacklist:", StringComparison.InvariantCulture))) { if (UiThemeUtils.IconTextButton(FontAwesomeIcon.Paste, "Paste")) { ImportBlacklistedQuests(text); } } } private void ImportBlacklistedQuests(string clipboardText) { try { string s = clipboardText.Substring("qst:blacklist:".Length); int skippedCount; List list = ElementId.FromStrings(Encoding.UTF8.GetString(Convert.FromBase64String(s)).Split(";", StringSplitOptions.RemoveEmptyEntries), out skippedCount); if (list.Count != 0) { base.Configuration.General.BlacklistedQuests.Clear(); base.Configuration.General.BlacklistedQuests.UnionWith(list); Save(); } } catch (Exception exception) { _logger.LogDebug(exception, "Failed to import blacklisted quests from clipboard"); } } private void DrawCurrentlyAcceptedQuests() { List currentlyAcceptedQuests = GetCurrentlyAcceptedQuests(); ImGui.Text("Currently Accepted Quests:"); using (ImRaii.ChildDisposable childDisposable = ImRaii.Child("AcceptedQuestsList", new Vector2(-1f, 100f), border: true)) { if ((bool)childDisposable) { if (currentlyAcceptedQuests.Count > 0) { using ImRaii.TableDisposable tableDisposable = UiThemeUtils.BeginThemedTable("AcceptedQuestsTable", 2); if ((bool)tableDisposable) { ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch); ImGui.TableSetupColumn("##Action", ImGuiTableColumnFlags.WidthFixed, 40f * ImGui.GetIO().FontGlobalScale); foreach (Quest item in currentlyAcceptedQuests) { ImGui.TableNextRow(); ImU8String id = new ImU8String(13, 1); id.AppendLiteral("AcceptedQuest"); id.AppendFormatted(item.Id); using (ImRaii.PushId(id)) { (Vector4, FontAwesomeIcon, string) questStyle = _uiUtils.GetQuestStyle(item.Id); bool flag = base.Configuration.General.BlacklistedQuests.Contains(item.Id); if (ImGui.TableNextColumn()) { bool flag2; using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push()) { ImGui.AlignTextToFramePadding(); ImGui.TextColored(flag ? UiThemeUtils.MutedTextColor : questStyle.Item1, questStyle.Item2.ToIconString()); flag2 = ImGui.IsItemHovered(); } ImGui.SameLine(); ImGui.AlignTextToFramePadding(); if (flag) { UiThemeUtils.RowLabel(item.Info.Name, 0f, UiThemeUtils.DimTextColor); } else { UiThemeUtils.RowLabel(item.Info.Name, 0f); } flag2 |= ImGui.IsItemHovered(); if (flag2) { _questTooltipComponent.Draw(item.Info); } } if (!ImGui.TableNextColumn()) { continue; } using (ImRaii.Disabled(flag)) { if (UiThemeUtils.IconButton(FontAwesomeIcon.Ban, ImGui.GetFrameHeight())) { base.Configuration.General.BlacklistedQuests.Add(item.Id); Save(); } } if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { ImGui.SetTooltip(flag ? "Quest already blacklisted" : "Add this quest to blacklist"); } } } } } else { UiThemeUtils.EmptyState(FontAwesomeIcon.BookOpen, "No quests currently accepted"); } } } ImGui.Spacing(); } private List GetCurrentlyAcceptedQuests() { if (Environment.TickCount64 < _nextAcceptedQuestsRefreshMs) { return _acceptedQuestsCache; } _nextAcceptedQuestsRefreshMs = Environment.TickCount64 + 1000; List list = new List(); try { foreach (Quest allQuest in _questRegistry.AllQuests) { if (_questFunctions.IsQuestAccepted(allQuest.Id)) { list.Add(allQuest); } } list.Sort((Quest a, Quest b) => string.Compare(a.Info.Name, b.Info.Name, StringComparison.OrdinalIgnoreCase)); } catch (Exception exception) { _logger.LogDebug(exception, "Failed to enumerate accepted quests"); list.Clear(); } _acceptedQuestsCache = list; return list; } }