qstbak/Questionable/Questionable.Windows.ConfigComponents/GeneralConfigComponent.cs
2025-11-17 11:31:27 +10:00

332 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using LLib.GameData;
using Lumina.Excel.Sheets;
using Questionable.Controller;
using Questionable.Data;
namespace Questionable.Windows.ConfigComponents;
internal sealed class GeneralConfigComponent : ConfigComponent
{
private static readonly List<(uint Id, string Name)> DefaultMounts;
private static readonly List<(EClassJob ClassJob, string Name)> DefaultClassJobs;
private readonly QuestRegistry _questRegistry;
private readonly TerritoryData _territoryData;
private readonly uint[] _mountIds;
private readonly string[] _mountNames;
private readonly string[] _combatModuleNames = new string[4] { "None", "Boss Mod (VBM)", "Wrath Combo", "Rotation Solver Reborn" };
private readonly string[] _grandCompanyNames = new string[4] { "None (manually pick quest)", "Maelstrom", "Twin Adder", "Immortal Flames" };
private readonly EClassJob[] _classJobIds;
private readonly string[] _classJobNames;
private string _mountSearchText = string.Empty;
private bool _mountComboJustOpened;
public GeneralConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, IDataManager dataManager, ClassJobUtils classJobUtils, QuestRegistry questRegistry, TerritoryData territoryData)
: base(pluginInterface, configuration)
{
_questRegistry = questRegistry;
_territoryData = territoryData;
List<(uint, string)> source = (from x in dataManager.GetExcelSheet<Mount>()
where x.RowId != 0 && x.Icon > 0
select (MountId: x.RowId, Name: CapitalizeName(x.Singular.ExtractText())) into x
where !string.IsNullOrEmpty(x.Name)
select x).OrderBy<(uint, string), string>(((uint MountId, string Name) x) => x.Name, StringComparer.OrdinalIgnoreCase).ToList();
_mountIds = DefaultMounts.Select<(uint, string), uint>(((uint Id, string Name) x) => x.Id).Concat(source.Select<(uint, string), uint>(((uint MountId, string Name) x) => x.MountId)).ToArray();
_mountNames = DefaultMounts.Select<(uint, string), string>(((uint Id, string Name) x) => x.Name).Concat(source.Select<(uint, string), string>(((uint MountId, string Name) x) => x.Name)).ToArray();
List<EClassJob> sortedClassJobs = classJobUtils.SortedClassJobs.Select(((EClassJob ClassJob, int Category) x) => x.ClassJob).ToList();
List<EClassJob> list = (from x in Enum.GetValues<EClassJob>()
where x != EClassJob.Adventurer
where !x.IsCrafter() && !x.IsGatherer()
where !x.IsClass()
orderby sortedClassJobs.IndexOf(x)
select x).ToList();
_classJobIds = DefaultClassJobs.Select<(EClassJob, string), EClassJob>(((EClassJob ClassJob, string Name) x) => x.ClassJob).Concat(list).ToArray();
_classJobNames = DefaultClassJobs.Select<(EClassJob, string), string>(((EClassJob ClassJob, string Name) x) => x.Name).Concat(list.Select((EClassJob x) => x.ToFriendlyString())).ToArray();
}
private static string CapitalizeName(string text)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
if (string.Equals(text, text.ToLowerInvariant(), StringComparison.Ordinal))
{
return CultureInfo.InvariantCulture.TextInfo.ToTitleCase(text);
}
string[] array = text.Split(' ');
for (int i = 0; i < array.Length; i++)
{
if (array[i].Length > 0 && char.IsLower(array[i][0]))
{
array[i] = char.ToUpperInvariant(array[i][0]) + array[i].Substring(1);
}
}
return string.Join(" ", array);
}
public override void DrawTab()
{
using ImRaii.IEndObject endObject = ImRaii.TabItem("General###General");
if (!endObject)
{
return;
}
int currentItem = (int)base.Configuration.General.CombatModule;
if (ImGui.Combo("Preferred Combat Module", ref currentItem, in _combatModuleNames, _combatModuleNames.Length))
{
base.Configuration.General.CombatModule = (Configuration.ECombatModule)currentItem;
Save();
}
int num = Array.FindIndex(_mountIds, (uint x) => x == base.Configuration.General.MountId);
if (num == -1)
{
num = 0;
base.Configuration.General.MountId = _mountIds[num];
Save();
}
string text = ((num >= 0 && num < _mountNames.Length) ? _mountNames[num] : "Unknown");
if (ImGui.BeginCombo("Preferred Mount", text, ImGuiComboFlags.HeightLarge))
{
if (!_mountComboJustOpened)
{
ImGui.SetKeyboardFocusHere();
_mountComboJustOpened = true;
}
ImGui.SetNextItemWidth(-1f);
ImGui.InputTextWithHint("##MountSearch", "Search mounts...", ref _mountSearchText, 256);
ImGui.Separator();
using (ImRaii.IEndObject endObject2 = ImRaii.Child("##MountScrollArea", new Vector2(0f, 300f), border: false))
{
if (endObject2)
{
string value = _mountSearchText.ToUpperInvariant();
for (int num2 = 0; num2 < _mountNames.Length; num2++)
{
if (string.IsNullOrEmpty(_mountSearchText) || _mountNames[num2].ToUpperInvariant().Contains(value, StringComparison.Ordinal))
{
bool flag = num2 == num;
if (ImGui.Selectable(_mountNames[num2], flag))
{
base.Configuration.General.MountId = _mountIds[num2];
Save();
_mountSearchText = string.Empty;
ImGui.CloseCurrentPopup();
}
if (flag)
{
ImGui.SetItemDefaultFocus();
}
}
}
}
}
ImGui.EndCombo();
}
else
{
_mountComboJustOpened = false;
}
int currentItem2 = (int)base.Configuration.General.GrandCompany;
if (ImGui.Combo("Preferred Grand Company", ref currentItem2, in _grandCompanyNames, _grandCompanyNames.Length))
{
base.Configuration.General.GrandCompany = (FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany)currentItem2;
Save();
}
int currentItem3 = Array.IndexOf(_classJobIds, base.Configuration.General.CombatJob);
if (currentItem3 == -1)
{
base.Configuration.General.CombatJob = EClassJob.Adventurer;
Save();
currentItem3 = 0;
}
if (ImGui.Combo("Preferred Combat Job", ref currentItem3, in _classJobNames, _classJobNames.Length))
{
base.Configuration.General.CombatJob = _classJobIds[currentItem3];
Save();
}
ImGui.Separator();
ImGui.Text("UI");
using (ImRaii.PushIndent())
{
bool v = base.Configuration.General.HideInAllInstances;
if (ImGui.Checkbox("Hide quest window in all instanced duties", ref v))
{
base.Configuration.General.HideInAllInstances = v;
Save();
}
bool v2 = base.Configuration.General.UseEscToCancelQuesting;
if (ImGui.Checkbox("Double tap ESC to cancel questing/movement", ref v2))
{
base.Configuration.General.UseEscToCancelQuesting = v2;
Save();
}
bool v3 = base.Configuration.General.ShowIncompleteSeasonalEvents;
if (ImGui.Checkbox("Show details for incomplete seasonal events", ref v3))
{
base.Configuration.General.ShowIncompleteSeasonalEvents = v3;
Save();
}
bool v4 = base.Configuration.General.HideSeasonalEventsFromJournalProgress;
if (ImGui.Checkbox("Hide Seasonal Events from Journal Progress", ref v4))
{
base.Configuration.General.HideSeasonalEventsFromJournalProgress = v4;
Save();
}
bool v5 = base.Configuration.General.ShowChangelogOnUpdate;
if (ImGui.Checkbox("Show changelog window when plugin updates", ref v5))
{
base.Configuration.General.ShowChangelogOnUpdate = v5;
Save();
}
}
ImGui.Separator();
ImGui.Text("Questing");
using (ImRaii.PushIndent())
{
bool v6 = base.Configuration.General.ConfigureTextAdvance;
if (ImGui.Checkbox("Automatically configure TextAdvance with the recommended settings", ref v6))
{
base.Configuration.General.ConfigureTextAdvance = v6;
Save();
}
bool v7 = base.Configuration.General.SkipLowPriorityDuties;
if (ImGui.Checkbox("Unlock certain optional dungeons and raids (instead of waiting for completion)", ref v7))
{
base.Configuration.General.SkipLowPriorityDuties = v7;
Save();
}
ImGui.SameLine();
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.TextDisabled(FontAwesomeIcon.InfoCircle.ToIconString());
}
if (ImGui.IsItemHovered())
{
using (ImRaii.Tooltip())
{
ImGui.Text("Questionable automatically picks up some optional quests (e.g. for aether currents, or the ARR alliance raids).");
ImGui.Text("If this setting is enabled, Questionable will continue with other quests, instead of waiting for manual completion of the duty.");
ImGui.Separator();
ImGui.Text("This affects the following dungeons and raids:");
foreach (var lowPriorityContentFinderConditionQuest in _questRegistry.LowPriorityContentFinderConditionQuests)
{
if (_territoryData.TryGetContentFinderCondition(lowPriorityContentFinderConditionQuest.ContentFinderConditionId, out TerritoryData.ContentFinderConditionData contentFinderConditionData))
{
ImU8String text2 = new ImU8String(0, 1);
text2.AppendFormatted(contentFinderConditionData.Name);
ImGui.BulletText(text2);
}
}
}
}
ImGui.Spacing();
bool v8 = base.Configuration.General.AutoStepRefreshEnabled;
if (ImGui.Checkbox("Automatically refresh quest steps when stuck", ref v8))
{
base.Configuration.General.AutoStepRefreshEnabled = v8;
Save();
}
ImGui.SameLine();
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.TextDisabled(FontAwesomeIcon.InfoCircle.ToIconString());
}
if (ImGui.IsItemHovered())
{
using (ImRaii.Tooltip())
{
ImGui.Text("Questionable will automatically refresh a quest step if it appears to be stuck after the configured delay.");
ImGui.Text("This helps resume automated quest completion when interruptions occur.");
}
}
using (ImRaii.Disabled(!v8))
{
ImGui.Indent();
int v9 = base.Configuration.General.AutoStepRefreshDelaySeconds;
ImGui.SetNextItemWidth(150f);
if (ImGui.SliderInt("Refresh delay (seconds)", ref v9, 10, 180))
{
base.Configuration.General.AutoStepRefreshDelaySeconds = v9;
Save();
}
Vector4 col = new Vector4(0.7f, 0.7f, 0.7f, 1f);
ImU8String text2 = new ImU8String(77, 1);
text2.AppendLiteral("Quest steps will refresh automatically after ");
text2.AppendFormatted(v9);
text2.AppendLiteral(" seconds if no progress is made.");
ImGui.TextColored(in col, text2);
ImGui.Unindent();
}
ImGui.Spacing();
ImGui.Separator();
ImGui.Text("Priority Quest Management");
bool v10 = base.Configuration.General.ClearPriorityQuestsOnLogout;
if (ImGui.Checkbox("Clear priority quests on character logout", ref v10))
{
base.Configuration.General.ClearPriorityQuestsOnLogout = v10;
Save();
}
bool v11 = base.Configuration.General.ClearPriorityQuestsOnCompletion;
if (ImGui.Checkbox("Remove priority quests when completed", ref v11))
{
base.Configuration.General.ClearPriorityQuestsOnCompletion = v11;
Save();
}
ImGui.SameLine();
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.TextDisabled(FontAwesomeIcon.InfoCircle.ToIconString());
}
if (ImGui.IsItemHovered())
{
using (ImRaii.Tooltip())
{
ImGui.Text("Automatically removes completed quests from your priority queue.");
ImGui.Text("This helps keep your priority list clean without manual intervention.");
return;
}
}
}
}
static GeneralConfigComponent()
{
int num = 1;
List<(uint, string)> list = new List<(uint, string)>(num);
CollectionsMarshal.SetCount(list, num);
Span<(uint, string)> span = CollectionsMarshal.AsSpan(list);
int index = 0;
span[index] = (0u, "Mount Roulette");
DefaultMounts = list;
index = 1;
List<(EClassJob, string)> list2 = new List<(EClassJob, string)>(index);
CollectionsMarshal.SetCount(list2, index);
Span<(EClassJob, string)> span2 = CollectionsMarshal.AsSpan(list2);
num = 0;
span2[num] = (EClassJob.Adventurer, "Auto (highest level/item level)");
DefaultClassJobs = list2;
}
}