using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Numerics; using System.Runtime.InteropServices; 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 LLib.GameData; using Lumina.Excel.Sheets; using Microsoft.Extensions.Logging; using Questionable.Controller; using Questionable.Data; using Questionable.Model; using Questionable.Model.Common; using Questionable.Model.Questing; namespace Questionable.Windows.ConfigComponents; internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent { private sealed record SinglePlayerDutyInfo(string Name, IQuestInfo QuestInfo, TerritoryData.ContentFinderConditionData ContentFinderConditionData, SinglePlayerDutyOptions Options, bool Enabled) { public EExpansionVersion Expansion => QuestInfo.Expansion; public uint JournalGenreId => (uint)(((int?)QuestInfo.JournalGenre) ?? (-1)); public ushort SortKey => QuestInfo.SortKey; public uint ContentFinderConditionId => ContentFinderConditionData.ContentFinderConditionId; public uint TerritoryId => ContentFinderConditionData.TerritoryId; public byte Index => Options.Index; public bool EnabledByDefault => Options.Enabled; public ReadOnlyCollection Notes => Options.Notes.AsReadOnly(); public bool IsLimsaStart { get { uint contentFinderConditionId = ContentFinderConditionId; if (contentFinderConditionId == 313 || contentFinderConditionId - 332 <= 2) { return true; } return false; } } public bool IsGridaniaStart { get { uint contentFinderConditionId = ContentFinderConditionId; if (contentFinderConditionId - 296 <= 3) { return true; } return false; } } public bool IsUldahStart { get { uint contentFinderConditionId = ContentFinderConditionId; if (contentFinderConditionId == 312 || contentFinderConditionId - 335 <= 2) { return true; } return false; } } public bool IsOtherRoleQuest { get { uint contentFinderConditionId = ContentFinderConditionId; if (contentFinderConditionId == 845 || contentFinderConditionId == 1016) { return true; } return false; } } } private const string SinglePlayerDutyClipboardPrefix = "qst:single:"; private static readonly List<(EClassJob ClassJob, string Name)> RoleQuestCategories; private readonly TerritoryData _territoryData; private readonly QuestRegistry _questRegistry; private readonly QuestData _questData; private readonly IDataManager _dataManager; private readonly ClassJobUtils _classJobUtils; private readonly ILogger _logger; private ImmutableDictionary> _startingCityBattles = ImmutableDictionary>.Empty; private ImmutableDictionary> _mainScenarioBattles = ImmutableDictionary>.Empty; private ImmutableDictionary> _jobQuestBattles = ImmutableDictionary>.Empty; private ImmutableDictionary> _roleQuestBattles = ImmutableDictionary>.Empty; private ImmutableList _otherRoleQuestBattles = ImmutableList.Empty; private ImmutableList<(string Label, List)> _otherQuestBattles = ImmutableList<(string, List)>.Empty; public SinglePlayerDutyConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, TerritoryData territoryData, QuestRegistry questRegistry, QuestData questData, IDataManager dataManager, ClassJobUtils classJobUtils, ILogger logger) : base(pluginInterface, configuration) { _territoryData = territoryData; _questRegistry = questRegistry; _questData = questData; _dataManager = dataManager; _classJobUtils = classJobUtils; _logger = logger; } public void Reload() { List list = (from x in _territoryData.GetAllQuestsWithQuestBattles() group x by x.QuestId into x where x.Count() > 1 select x.Key).ToList(); List list2 = new List(); Dictionary> dictionary = new Dictionary> { { EAetheryteLocation.Limsa, new List() }, { EAetheryteLocation.Gridania, new List() }, { EAetheryteLocation.Uldah, new List() } }; List list3 = new List(); Dictionary dictionary2 = (from x in Enum.GetValues() where x != EClassJob.Adventurer && !x.IsCrafter() && !x.IsGatherer() where x.IsClass() || !x.HasBaseClass() select x).SelectMany((EClassJob x) => from y in _questRegistry.GetKnownClassJobQuests(x, includeRoleQuests: false) select (QuestId: y.QuestId, ClassJob: x)).ToDictionary<(ElementId, EClassJob), ElementId, EClassJob>(((ElementId QuestId, EClassJob ClassJob) x) => x.QuestId, ((ElementId QuestId, EClassJob ClassJob) x) => x.ClassJob); Dictionary> dictionary3 = dictionary2.Values.Distinct().ToDictionary((EClassJob x) => x, (EClassJob _) => new List()); Dictionary> dictionary4 = (from x in RoleQuestCategories.SelectMany<(EClassJob, string), (ElementId, EClassJob)>(((EClassJob ClassJob, string Name) x) => from y in _questData.GetRoleQuests(x.ClassJob) select (QuestId: y.QuestId, ClassJob: x.ClassJob)) group x by x.QuestId).ToDictionary((IGrouping x) => x.Key, (IGrouping x) => x.Select(((ElementId QuestId, EClassJob ClassJob) y) => y.ClassJob).ToList()); Dictionary> dictionary5 = RoleQuestCategories.ToDictionary<(EClassJob, string), EClassJob, List>(((EClassJob ClassJob, string Name) x) => x.ClassJob, ((EClassJob ClassJob, string Name) _) => new List()); List list4 = new List(); foreach (var allQuestsWithQuestBattle in _territoryData.GetAllQuestsWithQuestBattles()) { ElementId item = allQuestsWithQuestBattle.QuestId; byte item2 = allQuestsWithQuestBattle.Index; TerritoryData.ContentFinderConditionData item3 = allQuestsWithQuestBattle.Data; IQuestInfo questInfo = _questData.GetQuestInfo(item); (bool Enabled, SinglePlayerDutyOptions Options) tuple = FindDutyOptions(item, item2); bool item4 = tuple.Enabled; SinglePlayerDutyOptions item5 = tuple.Options; string text = ConfigComponent.FormatLevel(questInfo.Level) + " " + questInfo.Name; if (!string.IsNullOrEmpty(item3.Name) && !questInfo.Name.EndsWith(item3.Name, StringComparison.Ordinal)) { text = text + " (" + item3.Name + ")"; } if (list.Contains(item)) { text += $" (Part {item5.Index + 1})"; } else { uint contentFinderConditionId = item3.ContentFinderConditionId; if ((contentFinderConditionId == 674 || contentFinderConditionId == 691) ? true : false) { text += " (Melee/Phys. Ranged)"; } } SinglePlayerDutyInfo singlePlayerDutyInfo = new SinglePlayerDutyInfo(text, questInfo, item3, item5, item4); EClassJob value; List value2; if (singlePlayerDutyInfo.IsLimsaStart) { dictionary[EAetheryteLocation.Limsa].Add(singlePlayerDutyInfo); } else if (singlePlayerDutyInfo.IsGridaniaStart) { dictionary[EAetheryteLocation.Gridania].Add(singlePlayerDutyInfo); } else if (singlePlayerDutyInfo.IsUldahStart) { dictionary[EAetheryteLocation.Uldah].Add(singlePlayerDutyInfo); } else if (questInfo.IsMainScenarioQuest) { list2.Add(singlePlayerDutyInfo); } else if (dictionary2.TryGetValue(item, out value)) { dictionary3[value].Add(singlePlayerDutyInfo); } else if (dictionary4.TryGetValue(item, out value2)) { foreach (EClassJob item6 in value2) { dictionary5[item6].Add(singlePlayerDutyInfo); } } else if (singlePlayerDutyInfo.IsOtherRoleQuest) { list4.Add(singlePlayerDutyInfo); } else { list3.Add(singlePlayerDutyInfo); } } _startingCityBattles = dictionary.ToImmutableDictionary((KeyValuePair> x) => x.Key, (KeyValuePair> x) => x.Value.OrderBy((SinglePlayerDutyInfo y) => y.SortKey).ToList()); _mainScenarioBattles = (from x in list2 group x by x.Expansion).ToImmutableDictionary((IGrouping x) => x.Key, (IGrouping x) => (from y in x orderby y.JournalGenreId, y.SortKey, y.Index select y).ToList()); _jobQuestBattles = dictionary3.Where((KeyValuePair> x) => x.Value.Count > 0).ToImmutableDictionary((KeyValuePair> x) => x.Key, (KeyValuePair> x) => (from y in x.Value.DistinctBy((SinglePlayerDutyInfo y) => y.ContentFinderConditionId) orderby y.JournalGenreId, y.SortKey, y.Index select y).ToList()); _roleQuestBattles = dictionary5.ToImmutableDictionary((KeyValuePair> x) => x.Key, (KeyValuePair> x) => (from y in x.Value orderby y.JournalGenreId, y.SortKey, y.Index select y).ToList()); _otherRoleQuestBattles = list4.ToImmutableList(); _otherQuestBattles = (from x in list3 orderby x.JournalGenreId, x.SortKey, x.Index group x by x.JournalGenreId into x select (BuildJournalGenreLabel(x.Key), x.ToList())).ToImmutableList(); } private (bool Enabled, SinglePlayerDutyOptions Options) FindDutyOptions(ElementId questId, byte index) { SinglePlayerDutyOptions singlePlayerDutyOptions = new SinglePlayerDutyOptions { Index = 0, Enabled = false }; if (_questRegistry.TryGetQuest(questId, out Questionable.Model.Quest quest)) { if (quest.Root.Disabled) { _logger.LogDebug("Disabling quest battle for quest {QuestId}, quest is disabled", questId); return (Enabled: false, Options: singlePlayerDutyOptions); } QuestStep questStep = (from x in quest.AllSteps() select x.Step).FirstOrDefault((QuestStep x) => x.InteractionType == EInteractionType.SinglePlayerDuty && x.SinglePlayerDutyIndex == index); if (questStep == null) { _logger.LogWarning("Disabling quest battle for quest {QuestId}, no battle with index {Index} found", questId, index); return (Enabled: false, Options: singlePlayerDutyOptions); } return (Enabled: true, Options: questStep.SinglePlayerDutyOptions ?? singlePlayerDutyOptions); } _logger.LogDebug("Disabling quest battle for quest {QuestId}, unknown quest", questId); return (Enabled: false, Options: singlePlayerDutyOptions); } private string BuildJournalGenreLabel(uint journalGenreId) { JournalGenre row = _dataManager.GetExcelSheet().GetRow(journalGenreId); JournalCategory value = row.JournalCategory.Value; string text = row.Name.ExtractText(); return value.Name.ExtractText() + " ※ " + text; } public override void DrawTab() { using ImRaii.IEndObject endObject = ImRaii.TabItem("Quest Battles###QuestBattles"); if (!endObject) { return; } bool v = base.Configuration.SinglePlayerDuties.RunSoloInstancesWithBossMod; if (ImGui.Checkbox("Run quest battles with BossMod", ref v)) { base.Configuration.SinglePlayerDuties.RunSoloInstancesWithBossMod = v; Save(); } using (ImRaii.PushIndent(ImGui.GetFrameHeight() + ImGui.GetStyle().ItemInnerSpacing.X)) { using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed)) { ImGui.TextUnformatted("Work in Progress:"); ImGui.BulletText("Will always use BossMod for combat (ignoring the configured combat module)."); ImGui.BulletText("Only a small subset of quest battles have been tested - most of which are in the MSQ."); ImGui.BulletText("When retrying a failed battle, it will always start at 'Normal' difficulty."); ImGui.BulletText("Please don't enable this option when using a BossMod fork (such as Reborn);\nwith the missing combat module configuration, it is unlikely to be compatible."); } } ImGui.Separator(); using (ImRaii.Disabled(!v)) { ImGui.Text("Questionable includes a default list of quest battles that work if BossMod is installed."); ImGui.Text("The included list of quest battles can change with each update."); ImGui.Separator(); ImGui.Text("You can override the settings for each individual quest battle:"); using ImRaii.IEndObject endObject2 = ImRaii.TabBar("QuestionableConfigTabs"); if (endObject2) { DrawMainScenarioConfigTable(); DrawJobQuestConfigTable(); DrawRoleQuestConfigTable(); DrawOtherQuestConfigTable(); } DrawEnableAllButton(); ImGui.SameLine(); DrawClipboardButtons(); ImGui.SameLine(); DrawResetButton(); } } private void DrawMainScenarioConfigTable() { (int enabledCount, int totalCount) mainScenarioQuestCounts = GetMainScenarioQuestCounts(); int item = mainScenarioQuestCounts.enabledCount; int item2 = mainScenarioQuestCounts.totalCount; ImU8String label = new ImU8String(30, 2); label.AppendLiteral("Main Scenario Quests ("); label.AppendFormatted(item); label.AppendLiteral("/"); label.AppendFormatted(item2); label.AppendLiteral(")###MSQ"); using ImRaii.IEndObject endObject = ImRaii.TabItem(label); if (!endObject) { return; } using ImRaii.IEndObject endObject2 = BeginChildArea(); if (!endObject2) { return; } (int enabledCount, int totalCount) questBattleCounts = GetQuestBattleCounts(_startingCityBattles[EAetheryteLocation.Limsa]); int item3 = questBattleCounts.enabledCount; int item4 = questBattleCounts.totalCount; string obj = $"Limsa Lominsa ({ConfigComponent.FormatLevel(5)} - {ConfigComponent.FormatLevel(14)}) ({item3}/{item4})"; string key = "Limsa"; ImGui.SetNextItemOpen(base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false), ImGuiCond.Always); if (ImGui.CollapsingHeader(obj)) { if (!base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key] = true; Save(); } DrawQuestTable("LimsaLominsa", _startingCityBattles[EAetheryteLocation.Limsa]); } else if (base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key] = false; Save(); } (int enabledCount, int totalCount) questBattleCounts2 = GetQuestBattleCounts(_startingCityBattles[EAetheryteLocation.Gridania]); int item5 = questBattleCounts2.enabledCount; int item6 = questBattleCounts2.totalCount; string obj2 = $"Gridania ({ConfigComponent.FormatLevel(5)} - {ConfigComponent.FormatLevel(14)}) ({item5}/{item6})"; string key2 = "Gridania"; ImGui.SetNextItemOpen(base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key2, defaultValue: false), ImGuiCond.Always); if (ImGui.CollapsingHeader(obj2)) { if (!base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key2, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key2] = true; Save(); } DrawQuestTable("Gridania", _startingCityBattles[EAetheryteLocation.Gridania]); } else if (base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key2, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key2] = false; Save(); } (int enabledCount, int totalCount) questBattleCounts3 = GetQuestBattleCounts(_startingCityBattles[EAetheryteLocation.Uldah]); int item7 = questBattleCounts3.enabledCount; int item8 = questBattleCounts3.totalCount; string obj3 = $"Ul'dah ({ConfigComponent.FormatLevel(4)} - {ConfigComponent.FormatLevel(14)}) ({item7}/{item8})"; string key3 = "Uldah"; ImGui.SetNextItemOpen(base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key3, defaultValue: false), ImGuiCond.Always); if (ImGui.CollapsingHeader(obj3)) { if (!base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key3, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key3] = true; Save(); } DrawQuestTable("Uldah", _startingCityBattles[EAetheryteLocation.Uldah]); } else if (base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key3, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key3] = false; Save(); } EExpansionVersion[] values = Enum.GetValues(); for (int i = 0; i < values.Length; i++) { EExpansionVersion eExpansionVersion = values[i]; if (!_mainScenarioBattles.TryGetValue(eExpansionVersion, out List value)) { continue; } (int enabledCount, int totalCount) questBattleCounts4 = GetQuestBattleCounts(value); int item9 = questBattleCounts4.enabledCount; int item10 = questBattleCounts4.totalCount; string obj4 = $"{eExpansionVersion.ToFriendlyString()} ({item9}/{item10})"; string key4 = eExpansionVersion.ToString(); ImGui.SetNextItemOpen(base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key4, defaultValue: false), ImGuiCond.Always); if (ImGui.CollapsingHeader(obj4)) { if (!base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key4, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key4] = true; Save(); } DrawQuestTable($"Duties{eExpansionVersion}", value); } else if (base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key4, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key4] = false; Save(); } } } private void DrawJobQuestConfigTable() { (int enabledCount, int totalCount) jobQuestCounts = GetJobQuestCounts(); int item = jobQuestCounts.enabledCount; int item2 = jobQuestCounts.totalCount; ImU8String label = new ImU8String(32, 2); label.AppendLiteral("Class/Job Quests ("); label.AppendFormatted(item); label.AppendLiteral("/"); label.AppendFormatted(item2); label.AppendLiteral(")###JobQuests"); using ImRaii.IEndObject endObject = ImRaii.TabItem(label); if (!endObject) { return; } using ImRaii.IEndObject endObject2 = BeginChildArea(); if (!endObject2) { return; } int num = 0; foreach (var (eClassJob, num2) in _classJobUtils.SortedClassJobs) { if (eClassJob.IsCrafter() || eClassJob.IsGatherer() || !_jobQuestBattles.TryGetValue(eClassJob, out List value)) { continue; } if (num2 != num) { num = num2; ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); } string text = eClassJob.ToFriendlyString(); if (eClassJob.IsClass()) { text = text + " / " + eClassJob.AsJob().ToFriendlyString(); } (int enabledCount, int totalCount) questBattleCounts = GetQuestBattleCounts(value); int item3 = questBattleCounts.enabledCount; int item4 = questBattleCounts.totalCount; string obj = $"{text} ({item3}/{item4})"; string key = eClassJob.ToString(); ImGui.SetNextItemOpen(base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false), ImGuiCond.Always); if (ImGui.CollapsingHeader(obj)) { if (!base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key] = true; Save(); } DrawQuestTable($"JobQuests{eClassJob}", value); } else if (base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key] = false; Save(); } } } private void DrawRoleQuestConfigTable() { (int enabledCount, int totalCount) roleQuestCounts = GetRoleQuestCounts(); int item = roleQuestCounts.enabledCount; int item2 = roleQuestCounts.totalCount; ImU8String label = new ImU8String(28, 2); label.AppendLiteral("Role Quests ("); label.AppendFormatted(item); label.AppendLiteral("/"); label.AppendFormatted(item2); label.AppendLiteral(")###RoleQuests"); using ImRaii.IEndObject endObject = ImRaii.TabItem(label); if (!endObject) { return; } using ImRaii.IEndObject endObject2 = BeginChildArea(); if (!endObject2) { return; } foreach (var (eClassJob, value) in RoleQuestCategories) { if (!_roleQuestBattles.TryGetValue(eClassJob, out List value2)) { continue; } (int enabledCount, int totalCount) questBattleCounts = GetQuestBattleCounts(value2); int item3 = questBattleCounts.enabledCount; int item4 = questBattleCounts.totalCount; string obj = $"{value} ({item3}/{item4})"; string key = $"Role_{eClassJob}"; ImGui.SetNextItemOpen(base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false), ImGuiCond.Always); if (ImGui.CollapsingHeader(obj)) { if (!base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key] = true; Save(); } DrawQuestTable($"RoleQuests{eClassJob}", value2); } else if (base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key] = false; Save(); } } (int enabledCount, int totalCount) questBattleCounts2 = GetQuestBattleCounts(_otherRoleQuestBattles); int item5 = questBattleCounts2.enabledCount; int item6 = questBattleCounts2.totalCount; string obj2 = $"General Role Quests ({item5}/{item6})"; string key2 = "Role_General"; ImGui.SetNextItemOpen(base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key2, defaultValue: false), ImGuiCond.Always); if (ImGui.CollapsingHeader(obj2)) { if (!base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key2, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key2] = true; Save(); } DrawQuestTable("RoleQuestsGeneral", _otherRoleQuestBattles); } else if (base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key2, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key2] = false; Save(); } } private void DrawOtherQuestConfigTable() { (int enabledCount, int totalCount) otherQuestCounts = GetOtherQuestCounts(); int item = otherQuestCounts.enabledCount; int item2 = otherQuestCounts.totalCount; ImU8String label = new ImU8String(29, 2); label.AppendLiteral("Other Quests ("); label.AppendFormatted(item); label.AppendLiteral("/"); label.AppendFormatted(item2); label.AppendLiteral(")###MiscQuests"); using ImRaii.IEndObject endObject = ImRaii.TabItem(label); if (!endObject) { return; } using ImRaii.IEndObject endObject2 = BeginChildArea(); if (!endObject2) { return; } foreach (var otherQuestBattle in _otherQuestBattles) { string item3 = otherQuestBattle.Label; List item4 = otherQuestBattle.Item2; (int enabledCount, int totalCount) questBattleCounts = GetQuestBattleCounts(item4); int item5 = questBattleCounts.enabledCount; int item6 = questBattleCounts.totalCount; string obj = $"{item3} ({item5}/{item6})"; string key = "Other_" + item3; ImGui.SetNextItemOpen(base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false), ImGuiCond.Always); if (ImGui.CollapsingHeader(obj)) { if (!base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key] = true; Save(); } DrawQuestTable("Other" + item3, item4); } else if (base.Configuration.SinglePlayerDuties.HeaderStates.GetValueOrDefault(key, defaultValue: false)) { base.Configuration.SinglePlayerDuties.HeaderStates[key] = false; Save(); } } } private void DrawQuestTable(string label, IReadOnlyList dutyInfos) { using ImRaii.IEndObject endObject = ImRaii.Table(label, 2, ImGuiTableFlags.SizingFixedFit); if (!ImRaii.IEndObject.op_True(endObject)) { return; } ImGui.TableSetupColumn("Quest", ImGuiTableColumnFlags.WidthStretch); ImGui.TableSetupColumn("Options", ImGuiTableColumnFlags.WidthFixed, 200f); foreach (SinglePlayerDutyInfo dutyInfo in dutyInfos) { ImGui.TableNextRow(); string[] items = (dutyInfo.EnabledByDefault ? SupportedCfcOptions : UnsupportedCfcOptions); int currentItem = 0; if (base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Contains(dutyInfo.ContentFinderConditionId)) { currentItem = 1; } if (base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Contains(dutyInfo.ContentFinderConditionId)) { currentItem = 2; } ImU8String text; if (ImGui.TableNextColumn()) { ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted(dutyInfo.Name); if (ImGui.IsItemHovered() && base.Configuration.Advanced.AdditionalStatusInformation) { using ImRaii.IEndObject endObject2 = ImRaii.Tooltip(); if (endObject2) { ImGui.TextUnformatted(dutyInfo.Name); ImGui.Separator(); text = new ImU8String(13, 1); text.AppendLiteral("TerritoryId: "); text.AppendFormatted(dutyInfo.TerritoryId); ImGui.BulletText(text); text = new ImU8String(26, 1); text.AppendLiteral("ContentFinderConditionId: "); text.AppendFormatted(dutyInfo.ContentFinderConditionId); ImGui.BulletText(text); } } if (!dutyInfo.Enabled) { ImGuiComponents.HelpMarker("Questionable doesn't include support for this quest.", FontAwesomeIcon.Times, ImGuiColors.DalamudRed); } else if (dutyInfo.Notes.Count > 0) { ConfigComponent.DrawNotes(dutyInfo.EnabledByDefault, dutyInfo.Notes); } } if (!ImGui.TableNextColumn()) { continue; } text = new ImU8String(6, 1); text.AppendLiteral("##Duty"); text.AppendFormatted(dutyInfo.ContentFinderConditionId); using (ImRaii.PushId(text)) { using (ImRaii.Disabled(!dutyInfo.Enabled)) { ImGui.SetNextItemWidth(200f); if (ImGui.Combo(string.Empty, ref currentItem, in items, items.Length)) { base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Remove(dutyInfo.ContentFinderConditionId); base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Remove(dutyInfo.ContentFinderConditionId); switch (currentItem) { case 1: base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Add(dutyInfo.ContentFinderConditionId); break; case 2: base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Add(dutyInfo.ContentFinderConditionId); break; } Save(); } } } } } private static ImRaii.IEndObject BeginChildArea() { return ImRaii.Child("DutyConfiguration", new Vector2(675f, 400f), border: true); } private void DrawEnableAllButton() { if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.CheckCircle, "Enable All")) { base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Clear(); base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Clear(); foreach (SinglePlayerDutyInfo allEnabledSinglePlayerDuty in GetAllEnabledSinglePlayerDuties()) { base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Add(allEnabledSinglePlayerDuty.ContentFinderConditionId); } Save(); } if (ImGui.IsItemHovered()) { ImGui.SetTooltip("Enable all of the quest battles, use at your own risk."); } } private void DrawClipboardButtons() { using (ImRaii.Disabled(base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Count + base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Count == 0)) { if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Copy, "Export to clipboard")) { IEnumerable first = base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Select((uint x) => $"{"+"}{x}"); IEnumerable second = base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Select((uint x) => $"{"-"}{x}"); ImGui.SetClipboardText("qst:single:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(";", first.Concat(second))))); } } ImGui.SameLine(); string text = ImGui.GetClipboardText().Trim(); using (ImRaii.Disabled(string.IsNullOrEmpty(text) || !text.StartsWith("qst:single:", StringComparison.InvariantCulture))) { if (!ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Paste, "Import from Clipboard")) { return; } text = text.Substring("qst:single:".Length); string text2 = Encoding.UTF8.GetString(Convert.FromBase64String(text)); base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Clear(); base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Clear(); string[] array = text2.Split(";"); foreach (string text3 in array) { if (text3.StartsWith("+", StringComparison.InvariantCulture) && uint.TryParse(text3.AsSpan("+".Length), CultureInfo.InvariantCulture, out var result)) { base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Add(result); } if (text3.StartsWith("-", StringComparison.InvariantCulture) && uint.TryParse(text3.AsSpan("-".Length), CultureInfo.InvariantCulture, out var result2)) { base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Add(result2); } } Save(); } } private void DrawResetButton() { using (ImRaii.Disabled(!ImGui.IsKeyDown(ImGuiKey.ModCtrl))) { if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Undo, "Reset to default")) { base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Clear(); base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Clear(); Save(); } } if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) { ImGui.SetTooltip("Hold CTRL to enable this button."); } } private IEnumerable GetAllEnabledSinglePlayerDuties() { return from x in _startingCityBattles.Values.SelectMany((List x) => x).Concat(_mainScenarioBattles.Values.SelectMany((List x) => x)).Concat(_jobQuestBattles.Values.SelectMany((List x) => x)) .Concat(_roleQuestBattles.Values.SelectMany((List x) => x)) .Concat(_otherRoleQuestBattles) .Concat(_otherQuestBattles.SelectMany<(string, List), SinglePlayerDutyInfo>(((string Label, List) x) => x.Item2)) where x.Enabled select x; } private (int enabledCount, int totalCount) GetQuestBattleCounts(IReadOnlyList dutyInfos) { int num = 0; int num2 = 0; foreach (SinglePlayerDutyInfo dutyInfo in dutyInfos) { if (dutyInfo.Enabled) { num2++; if (base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Contains(dutyInfo.ContentFinderConditionId) || (!base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Contains(dutyInfo.ContentFinderConditionId) && dutyInfo.EnabledByDefault)) { num++; } } } return (enabledCount: num, totalCount: num2); } private (int enabledCount, int totalCount) GetMainScenarioQuestCounts() { int num = 0; int num2 = 0; foreach (List value in _startingCityBattles.Values) { (int enabledCount, int totalCount) questBattleCounts = GetQuestBattleCounts(value); int item = questBattleCounts.enabledCount; int item2 = questBattleCounts.totalCount; num += item; num2 += item2; } foreach (List value2 in _mainScenarioBattles.Values) { (int enabledCount, int totalCount) questBattleCounts2 = GetQuestBattleCounts(value2); int item3 = questBattleCounts2.enabledCount; int item4 = questBattleCounts2.totalCount; num += item3; num2 += item4; } return (enabledCount: num, totalCount: num2); } private (int enabledCount, int totalCount) GetJobQuestCounts() { int num = 0; int num2 = 0; foreach (List value in _jobQuestBattles.Values) { (int enabledCount, int totalCount) questBattleCounts = GetQuestBattleCounts(value); int item = questBattleCounts.enabledCount; int item2 = questBattleCounts.totalCount; num += item; num2 += item2; } return (enabledCount: num, totalCount: num2); } private (int enabledCount, int totalCount) GetRoleQuestCounts() { int num = 0; int num2 = 0; foreach (List value in _roleQuestBattles.Values) { (int enabledCount, int totalCount) questBattleCounts = GetQuestBattleCounts(value); int item = questBattleCounts.enabledCount; int item2 = questBattleCounts.totalCount; num += item; num2 += item2; } (int enabledCount, int totalCount) questBattleCounts2 = GetQuestBattleCounts(_otherRoleQuestBattles); int item3 = questBattleCounts2.enabledCount; int item4 = questBattleCounts2.totalCount; num += item3; num2 += item4; return (enabledCount: num, totalCount: num2); } private (int enabledCount, int totalCount) GetOtherQuestCounts() { int num = 0; int num2 = 0; foreach (var otherQuestBattle in _otherQuestBattles) { List item = otherQuestBattle.Item2; (int enabledCount, int totalCount) questBattleCounts = GetQuestBattleCounts(item); int item2 = questBattleCounts.enabledCount; int item3 = questBattleCounts.totalCount; num += item2; num2 += item3; } return (enabledCount: num, totalCount: num2); } static SinglePlayerDutyConfigComponent() { int num = 5; List<(EClassJob, string)> list = new List<(EClassJob, string)>(num); CollectionsMarshal.SetCount(list, num); Span<(EClassJob, string)> span = CollectionsMarshal.AsSpan(list); int num2 = 0; span[num2] = (EClassJob.Paladin, "Tank Role Quests"); num2++; span[num2] = (EClassJob.WhiteMage, "Healer Role Quests"); num2++; span[num2] = (EClassJob.Lancer, "Melee Role Quests"); num2++; span[num2] = (EClassJob.Bard, "Physical Ranged Role Quests"); num2++; span[num2] = (EClassJob.BlackMage, "Magical Ranged Role Quests"); RoleQuestCategories = list; } }