using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Dalamud.Bindings.ImGui; using Dalamud.Interface; 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.Questing; using SmartNav.Model; namespace Questionable.Windows.ConfigComponents; internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent { private sealed record SinglePlayerDutyInfo(string Name, IQuestInfo QuestInfo, TerritoryData.ContentFinderConditionData ContentFinderConditionData, byte Index, bool EnabledByDefault, string[] Notes, 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 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 string[] _retryDifficulties = new string[3] { "Normal", "Easy", "Very Easy" }; 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; private bool _needsReload = true; private int _selectedTab; 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; _questRegistry.Reloaded += delegate { _needsReload = true; }; } 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 QuestExists, bool EnabledByDefault, byte Index, string[] Notes) tuple = FindDutyOptions(item, item2); bool item4 = tuple.QuestExists; bool item5 = tuple.EnabledByDefault; string[] item6 = tuple.Notes; 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 {item2 + 1})"; } else { uint contentFinderConditionId = item3.ContentFinderConditionId; if ((contentFinderConditionId == 674 || contentFinderConditionId == 691) ? true : false) { text += " (Melee/Phys. Ranged)"; } } SinglePlayerDutyInfo singlePlayerDutyInfo = new SinglePlayerDutyInfo(text, questInfo, item3, item2, item5, item6, 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 item7 in value2) { dictionary5[item7].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 QuestExists, bool EnabledByDefault, byte Index, string[] Notes) FindDutyOptions(ElementId questId, byte index) { if (!_questRegistry.TryGetQuest(questId, out Questionable.Model.Quest quest)) { _logger.LogDebug("Disabling quest battle for quest {QuestId}, unknown quest", questId); return (QuestExists: false, EnabledByDefault: false, Index: index, Notes: Array.Empty()); } if (quest.Root.Disabled) { _logger.LogDebug("Disabling quest battle for quest {QuestId}, quest is disabled", questId); return (QuestExists: false, EnabledByDefault: false, Index: index, Notes: Array.Empty()); } if ((from x in quest.AllSteps() select x.Step).FirstOrDefault((QuestStep x) => x.InteractionType == EInteractionType.SinglePlayerDuty && x.SinglePlayerDutyIndex == index) == null) { _logger.LogWarning("Disabling quest battle for quest {QuestId}, no battle with index {Index} found", questId, index); return (QuestExists: false, EnabledByDefault: false, Index: index, Notes: Array.Empty()); } ushort key = (ushort)((questId is QuestId questId2) ? questId2.Value : 0); bool item = false; string[] item2 = Array.Empty(); if (DutyRegistry.SinglePlayerDuties.TryGetValue(key, out SinglePlayerDutyEntry[] value)) { SinglePlayerDutyEntry singlePlayerDutyEntry = value.FirstOrDefault((SinglePlayerDutyEntry e) => e.Index == index); if (singlePlayerDutyEntry != null) { item = singlePlayerDutyEntry.Enabled; item2 = singlePlayerDutyEntry.Notes; } } return (QuestExists: true, EnabledByDefault: item, Index: index, Notes: item2); } 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() { if (_needsReload) { _needsReload = false; Reload(); } UiThemeUtils.SectionHeader("Quest Battle Settings"); ImGui.Spacing(); (Vector2 ContentStartPos, float AvailableWidth, ImDrawListPtr DrawList) tuple = UiThemeUtils.BeginCard(); Vector2 item = tuple.ContentStartPos; float item2 = tuple.AvailableWidth; ImDrawListPtr item3 = tuple.DrawList; bool value = base.Configuration.SinglePlayerDuties.RunSoloInstancesWithBossMod; if (UiThemeUtils.WrappedCheckbox("Run quest battles with BossMod", ref value, "Work in Progress:\n- Will always use BossMod for combat (ignoring the configured combat module).\n- Only a small subset of quest battles have been tested - most of which are in the MSQ.")) { base.Configuration.SinglePlayerDuties.RunSoloInstancesWithBossMod = value; Save(); } using (ImRaii.Disabled(!value)) { ImGui.Spacing(); int currentItem = (int)base.Configuration.SinglePlayerDuties.RetryDifficulty; ImGui.SetNextItemWidth(MathF.Min(ImGui.GetContentRegionAvail().X * 0.4f, 260f)); if (UiThemeUtils.ThemedCombo("Difficulty when retrying a failed quest battle", ref currentItem, _retryDifficulties, _retryDifficulties.Length)) { base.Configuration.SinglePlayerDuties.RetryDifficulty = (Configuration.ERetryDifficulty)currentItem; Save(); } ImGui.SameLine(); UiThemeUtils.InfoIcon("Not all quest battles offer Easy or Very Easy difficulty."); ImGui.Spacing(); int maxRetries = base.Configuration.SinglePlayerDuties.MaxRetries; if (ImGui.RadioButton("Do not retry failed quest battles", maxRetries == 0)) { base.Configuration.SinglePlayerDuties.MaxRetries = 0; Save(); } bool flag = maxRetries > 0; if (ImGui.RadioButton("Retry", flag)) { base.Configuration.SinglePlayerDuties.MaxRetries = Math.Max(1, maxRetries); Save(); } if (flag) { ImGui.SameLine(); ImGui.SetNextItemWidth(80f); int data = maxRetries; if (ImGui.InputInt("time(s)##retryCount", ref data, 1) && data >= 1) { base.Configuration.SinglePlayerDuties.MaxRetries = data; Save(); } } if (ImGui.RadioButton("Retry indefinitely", maxRetries == -1)) { base.Configuration.SinglePlayerDuties.MaxRetries = -1; Save(); } if (maxRetries == -1) { ImGui.SameLine(); UiThemeUtils.InfoIcon("Warning: Your gear may break if you retry endlessly!"); } } UiThemeUtils.EndCard(item, item2, item3); UiThemeUtils.SectionSpacing(); using (ImRaii.Disabled(!value)) { UiThemeUtils.SectionHeader("Quest Battle Overrides"); ImGui.Spacing(); (Vector2 ContentStartPos, float AvailableWidth, ImDrawListPtr DrawList) tuple2 = UiThemeUtils.BeginCard(); Vector2 item4 = tuple2.ContentStartPos; float item5 = tuple2.AvailableWidth; ImDrawListPtr item6 = tuple2.DrawList; (int enabledCount, int totalCount) mainScenarioQuestCounts = GetMainScenarioQuestCounts(); int item7 = mainScenarioQuestCounts.enabledCount; int item8 = mainScenarioQuestCounts.totalCount; (int enabledCount, int totalCount) jobQuestCounts = GetJobQuestCounts(); int item9 = jobQuestCounts.enabledCount; int item10 = jobQuestCounts.totalCount; (int enabledCount, int totalCount) roleQuestCounts = GetRoleQuestCounts(); int item11 = roleQuestCounts.enabledCount; int item12 = roleQuestCounts.totalCount; (int enabledCount, int totalCount) otherQuestCounts = GetOtherQuestCounts(); int item13 = otherQuestCounts.enabledCount; int item14 = otherQuestCounts.totalCount; InlineArray4 buffer = default(InlineArray4); global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef, string>(ref buffer, 0) = $"Main Scenario Quests ({item7}/{item8})"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef, string>(ref buffer, 1) = $"Class/Job Quests ({item9}/{item10})"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef, string>(ref buffer, 2) = $"Role Quests ({item11}/{item12})"; global::_003CPrivateImplementationDetails_003E.InlineArrayElementRef, string>(ref buffer, 3) = $"Other Quests ({item13}/{item14})"; ReadOnlySpan labels = global::_003CPrivateImplementationDetails_003E.InlineArrayAsReadOnlySpan, string>(in buffer, 4); _selectedTab = UiThemeUtils.DrawTabBar(labels, _selectedTab); ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y); switch (_selectedTab) { case 0: DrawMainScenarioConfigTable(); break; case 1: DrawJobQuestConfigTable(); break; case 2: DrawRoleQuestConfigTable(); break; case 3: DrawOtherQuestConfigTable(); break; } DrawEnableAllButton(); ImGui.SameLine(); DrawClipboardButtons("qst:single:", base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds, base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds); ImGui.SameLine(); if (UiThemeUtils.DestructiveButton(FontAwesomeIcon.Undo, "Reset to defaults")) { base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Clear(); base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Clear(); Save(); } UiThemeUtils.EndCard(item4, item5, item6); } } private void DrawMainScenarioConfigTable() { using ImRaii.ChildDisposable childDisposable = BeginChildArea(); if (!childDisposable) { return; } Dictionary headerStates = base.Configuration.SinglePlayerDuties.HeaderStates; var (value, value2) = GetQuestBattleCounts(_startingCityBattles[EAetheryteLocation.Limsa]); if (DrawPersistentAccordion($"Limsa Lominsa ({ConfigComponent.FormatLevel(5)} - {ConfigComponent.FormatLevel(14)})", $"{value}/{value2}", "Limsa", headerStates)) { DrawQuestTable("LimsaLominsa", _startingCityBattles[EAetheryteLocation.Limsa]); } var (value3, value4) = GetQuestBattleCounts(_startingCityBattles[EAetheryteLocation.Gridania]); if (DrawPersistentAccordion($"Gridania ({ConfigComponent.FormatLevel(5)} - {ConfigComponent.FormatLevel(14)})", $"{value3}/{value4}", "Gridania", headerStates)) { DrawQuestTable("Gridania", _startingCityBattles[EAetheryteLocation.Gridania]); } var (value5, value6) = GetQuestBattleCounts(_startingCityBattles[EAetheryteLocation.Uldah]); if (DrawPersistentAccordion($"Ul'dah ({ConfigComponent.FormatLevel(4)} - {ConfigComponent.FormatLevel(14)})", $"{value5}/{value6}", "Uldah", headerStates)) { DrawQuestTable("Uldah", _startingCityBattles[EAetheryteLocation.Uldah]); } EExpansionVersion[] values = Enum.GetValues(); for (int i = 0; i < values.Length; i++) { EExpansionVersion eExpansionVersion = values[i]; if (_mainScenarioBattles.TryGetValue(eExpansionVersion, out List value7)) { var (value8, value9) = GetQuestBattleCounts(value7); if (DrawPersistentAccordion(eExpansionVersion.ToFriendlyString(), $"{value8}/{value9}", eExpansionVersion.ToString(), headerStates)) { DrawQuestTable($"Duties{eExpansionVersion}", value7); } } } } private void DrawJobQuestConfigTable() { using ImRaii.ChildDisposable childDisposable = BeginChildArea(); if (!childDisposable) { return; } Dictionary headerStates = base.Configuration.SinglePlayerDuties.HeaderStates; int num = 0; foreach (var (eClassJob, num2) in _classJobUtils.SortedClassJobs) { if (!eClassJob.IsCrafter() && !eClassJob.IsGatherer() && _jobQuestBattles.TryGetValue(eClassJob, out List value)) { if (num2 != num) { num = num2; ImGui.Spacing(); ImGui.Separator(); ImGui.Spacing(); } string text = eClassJob.ToFriendlyString(); if (eClassJob.IsClass()) { text = text + " / " + eClassJob.AsJob().ToFriendlyString(); } var (value2, value3) = GetQuestBattleCounts(value); if (DrawPersistentAccordion(text, $"{value2}/{value3}", eClassJob.ToString(), headerStates)) { DrawQuestTable($"JobQuests{eClassJob}", value); } } } } private void DrawRoleQuestConfigTable() { using ImRaii.ChildDisposable childDisposable = BeginChildArea(); if (!childDisposable) { return; } Dictionary headerStates = base.Configuration.SinglePlayerDuties.HeaderStates; foreach (var (eClassJob, label) in RoleQuestCategories) { if (_roleQuestBattles.TryGetValue(eClassJob, out List value)) { var (value2, value3) = GetQuestBattleCounts(value); if (DrawPersistentAccordion(label, $"{value2}/{value3}", $"Role_{eClassJob}", headerStates)) { DrawQuestTable($"RoleQuests{eClassJob}", value); } } } var (value4, value5) = GetQuestBattleCounts(_otherRoleQuestBattles); if (DrawPersistentAccordion("General Role Quests", $"{value4}/{value5}", "Role_General", headerStates)) { DrawQuestTable("RoleQuestsGeneral", _otherRoleQuestBattles); } } private void DrawOtherQuestConfigTable() { using ImRaii.ChildDisposable childDisposable = BeginChildArea(); if (!childDisposable) { return; } Dictionary headerStates = base.Configuration.SinglePlayerDuties.HeaderStates; foreach (var (text, dutyInfos) in _otherQuestBattles) { var (value, value2) = GetQuestBattleCounts(dutyInfos); if (DrawPersistentAccordion(text, $"{value}/{value2}", "Other_" + text, headerStates)) { DrawQuestTable("Other" + text, dutyInfos); } } } private void DrawQuestTable(string label, IReadOnlyList dutyInfos) { using ImRaii.TableDisposable tableDisposable = UiThemeUtils.BeginThemedTable(label, 2, ImGuiTableFlags.SizingFixedFit); if (!(bool)tableDisposable) { return; } ImGui.TableSetupColumn("Quest", ImGuiTableColumnFlags.WidthStretch); ImGui.TableSetupColumn("Options", ImGuiTableColumnFlags.WidthFixed, 200f); foreach (SinglePlayerDutyInfo dutyInfo in dutyInfos) { ImGui.TableNextRow(); string[] array = (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; } if (ImGui.TableNextColumn()) { ImGui.AlignTextToFramePadding(); float reservedRightWidth = ((!dutyInfo.Enabled || dutyInfo.Notes.Length != 0) ? (ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.X) : 0f); UiThemeUtils.RowLabel(dutyInfo.Name, reservedRightWidth); if (!dutyInfo.Enabled) { ImGui.SameLine(); using (ImRaii.PushFont(UiBuilder.IconFont)) { ImGui.TextColored(in UiThemeUtils.StatusLocked, FontAwesomeIcon.Times.ToIconString()); } if (ImGui.IsItemHovered()) { using (ImRaii.Tooltip()) { ImGui.PushTextWrapPos(400f); ImGui.Text("Questionable doesn't include support for this quest."); ImGui.PopTextWrapPos(); } } } else if (dutyInfo.Notes.Length != 0) { ConfigComponent.DrawNotes(dutyInfo.EnabledByDefault, dutyInfo.Notes); } } if (!ImGui.TableNextColumn()) { continue; } ImU8String id = new ImU8String(6, 1); id.AppendLiteral("##Duty"); id.AppendFormatted(dutyInfo.ContentFinderConditionId); using (ImRaii.PushId(id)) { using (ImRaii.Disabled(!dutyInfo.Enabled)) { ImGui.SetNextItemWidth(-1f); if (UiThemeUtils.ThemedCombo(string.Empty, ref currentItem, array, array.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.ChildDisposable BeginChildArea() { Vector2 contentRegionAvail = ImGui.GetContentRegionAvail(); return ImRaii.Child("DutyConfiguration", new Vector2(contentRegionAvail.X, MathF.Max(contentRegionAvail.Y - 30f - 12f, 200f)), border: true); } private bool DrawPersistentAccordion(string label, string? rightText, string stateKey, Dictionary headerStates) { bool open = headerStates.GetValueOrDefault(stateKey, defaultValue: false); UiThemeUtils.ThemedAccordion(stateKey, label, rightText, ref open); if (open != headerStates.GetValueOrDefault(stateKey, defaultValue: false)) { headerStates[stateKey] = open; Save(); } return open; } private void DrawEnableAllButton() { if (UiThemeUtils.IconTextButton(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 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); span[0] = (EClassJob.Paladin, "Tank Role Quests"); span[1] = (EClassJob.WhiteMage, "Healer Role Quests"); span[2] = (EClassJob.Lancer, "Melee Role Quests"); span[3] = (EClassJob.Bard, "Physical Ranged Role Quests"); span[4] = (EClassJob.BlackMage, "Magical Ranged Role Quests"); RoleQuestCategories = list; } }