qstbak/Questionable/Questionable.Windows.ConfigComponents/DutyConfigComponent.cs
2025-11-19 12:01:37 +10:00

400 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
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.Plugin;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using Lumina.Excel.Sheets;
using Questionable.Controller;
using Questionable.Data;
using Questionable.External;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Windows.ConfigComponents;
internal sealed class DutyConfigComponent : ConfigComponent
{
private sealed record DutyInfo(uint CfcId, uint TerritoryId, string Name);
private const string DutyClipboardPrefix = "qst:duty:";
private const string ModePrefix = "M:";
private static readonly string[] DutyModeLabels = new string[3] { "Duty Support", "Unsync (Solo)", "Unsync (Party)" };
private readonly QuestRegistry _questRegistry;
private readonly AutoDutyIpc _autoDutyIpc;
private readonly Dictionary<EExpansionVersion, List<DutyInfo>> _contentFinderConditionNames;
public DutyConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, IDataManager dataManager, QuestRegistry questRegistry, AutoDutyIpc autoDutyIpc, TerritoryData territoryData)
: base(pluginInterface, configuration)
{
_questRegistry = questRegistry;
_autoDutyIpc = autoDutyIpc;
_contentFinderConditionNames = (from x in dataManager.GetExcelSheet<DawnContent>()
where x.RowId != 0 && !x.Unknown16
orderby x.Unknown15
select x.Content.ValueNullable into x
where x.HasValue
select x.Value into x
select new
{
Expansion = (EExpansionVersion)x.TerritoryType.Value.ExVersion.RowId,
CfcId = x.RowId,
Name = (territoryData.GetContentFinderCondition(x.RowId)?.Name ?? "?"),
TerritoryId = x.TerritoryType.RowId,
ContentType = x.ContentType.RowId,
Level = x.ClassJobLevelRequired,
SortKey = x.SortKey
} into x
group x by x.Expansion).ToDictionary(x => x.Key, x => x.Select(y => new DutyInfo(y.CfcId, y.TerritoryId, ConfigComponent.FormatLevel(y.Level) + " " + y.Name)).ToList());
}
public override void DrawTab()
{
using ImRaii.IEndObject endObject = ImRaii.TabItem("Duties###Duties");
if (!endObject)
{
return;
}
bool v = base.Configuration.Duties.RunInstancedContentWithAutoDuty;
if (ImGui.Checkbox("Run instanced content with AutoDuty and BossMod", ref v))
{
base.Configuration.Duties.RunInstancedContentWithAutoDuty = v;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("The combat module used for this is configured by AutoDuty, ignoring whichever selection you've made in Questionable's \"General\" configuration.");
using (ImRaii.Disabled(!v))
{
ImGui.Spacing();
ImGui.Text("Default duty mode:");
ImGui.SameLine();
int currentItem = (int)base.Configuration.Duties.DefaultDutyMode;
ImGui.SetNextItemWidth(200f);
if (ImGui.Combo("##DefaultDutyMode", ref currentItem, in DutyModeLabels, DutyModeLabels.Length))
{
base.Configuration.Duties.DefaultDutyMode = (EDutyMode)currentItem;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("\ufffd Duty Support: Run with NPC party members (level synced)\n\ufffd Unsync (Solo): Run alone at your current level (unsynced)\n\ufffd Unsync (Party): Run with your party at current level (unsynced)\n\nYou can override this setting for individual duties below.");
}
ImGui.Separator();
using (ImRaii.Disabled(!v))
{
ImGui.Text("Questionable includes a default list of duties that work if AutoDuty and BossMod are installed.");
ImGui.Text("The included list of duties can change with each update, and is based on the following spreadsheet:");
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.GlobeEurope, "Open AutoDuty spreadsheet"))
{
Util.OpenLink("https://docs.google.com/spreadsheets/d/151RlpqRcCpiD_VbQn6Duf-u-S71EP7d0mx3j1PDNoNA/edit?pli=1#gid=0");
}
ImGui.Separator();
ImGui.Text("You can override the settings for each individual dungeon/trial:");
DrawConfigTable(v);
DrawEnableAllButton();
ImGui.SameLine();
DrawClipboardButtons();
ImGui.SameLine();
DrawResetButton();
}
}
private void DrawConfigTable(bool runInstancedContentWithAutoDuty)
{
using ImRaii.IEndObject endObject = ImRaii.Child("DutyConfiguration", new Vector2(950f, 400f), border: true);
if (!endObject)
{
return;
}
EExpansionVersion[] values = Enum.GetValues<EExpansionVersion>();
for (int i = 0; i < values.Length; i++)
{
EExpansionVersion eExpansionVersion = values[i];
(int enabledCount, int totalCount) dutyCountsForExpansion = GetDutyCountsForExpansion(eExpansionVersion);
int item = dutyCountsForExpansion.enabledCount;
int item2 = dutyCountsForExpansion.totalCount;
string obj = ((item2 > 0) ? $"{eExpansionVersion.ToFriendlyString()} ({item}/{item2})" : eExpansionVersion.ToFriendlyString());
string key = eExpansionVersion.ToString();
ImGui.SetNextItemOpen(base.Configuration.Duties.ExpansionHeaderStates.GetValueOrDefault(key, defaultValue: false), ImGuiCond.Always);
if (ImGui.CollapsingHeader(obj))
{
if (!base.Configuration.Duties.ExpansionHeaderStates.GetValueOrDefault(key, defaultValue: false))
{
base.Configuration.Duties.ExpansionHeaderStates[key] = true;
Save();
}
ImU8String table = new ImU8String(6, 1);
table.AppendLiteral("Duties");
table.AppendFormatted(eExpansionVersion);
using ImRaii.IEndObject endObject2 = ImRaii.Table(table, 3, ImGuiTableFlags.SizingFixedFit);
if (!ImRaii.IEndObject.op_True(endObject2))
{
continue;
}
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("Enabled", ImGuiTableColumnFlags.WidthFixed, 200f);
ImGui.TableSetupColumn("Mode", ImGuiTableColumnFlags.WidthFixed, 150f);
if (!_contentFinderConditionNames.TryGetValue(eExpansionVersion, out List<DutyInfo> value))
{
continue;
}
foreach (DutyInfo item3 in value)
{
item3.Deconstruct(out uint CfcId, out uint TerritoryId, out string Name);
uint num = CfcId;
uint value2 = TerritoryId;
string text = Name;
if (!_questRegistry.TryGetDutyByContentFinderConditionId(num, out DutyOptions dutyOptions))
{
continue;
}
ImGui.TableNextRow();
string[] items = (dutyOptions.Enabled ? SupportedCfcOptions : UnsupportedCfcOptions);
int currentItem = 0;
if (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(num))
{
currentItem = 1;
}
if (base.Configuration.Duties.BlacklistedDutyCfcIds.Contains(num))
{
currentItem = 2;
}
if (ImGui.TableNextColumn())
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(text);
if (ImGui.IsItemHovered() && base.Configuration.Advanced.AdditionalStatusInformation)
{
using ImRaii.IEndObject endObject3 = ImRaii.Tooltip();
if (endObject3)
{
ImGui.TextUnformatted(text);
ImGui.Separator();
table = new ImU8String(13, 1);
table.AppendLiteral("TerritoryId: ");
table.AppendFormatted(value2);
ImGui.BulletText(table);
table = new ImU8String(26, 1);
table.AppendLiteral("ContentFinderConditionId: ");
table.AppendFormatted(num);
ImGui.BulletText(table);
}
}
if (runInstancedContentWithAutoDuty && !_autoDutyIpc.HasPath(num))
{
ImGuiComponents.HelpMarker("This duty is not supported by AutoDuty", FontAwesomeIcon.Times, ImGuiColors.DalamudRed);
}
else if (dutyOptions.Notes.Count > 0)
{
ConfigComponent.DrawNotes(dutyOptions.Enabled, dutyOptions.Notes);
}
}
if (ImGui.TableNextColumn())
{
table = new ImU8String(16, 1);
table.AppendLiteral("##DungeonEnabled");
table.AppendFormatted(num);
using (ImRaii.PushId(table))
{
ImGui.SetNextItemWidth(200f);
if (ImGui.Combo(string.Empty, ref currentItem, in items, items.Length))
{
base.Configuration.Duties.WhitelistedDutyCfcIds.Remove(num);
base.Configuration.Duties.BlacklistedDutyCfcIds.Remove(num);
switch (currentItem)
{
case 1:
base.Configuration.Duties.WhitelistedDutyCfcIds.Add(num);
break;
case 2:
base.Configuration.Duties.BlacklistedDutyCfcIds.Add(num);
break;
}
Save();
}
}
}
if (!ImGui.TableNextColumn())
{
continue;
}
table = new ImU8String(13, 1);
table.AppendLiteral("##DungeonMode");
table.AppendFormatted(num);
using (ImRaii.PushId(table))
{
EDutyMode value3;
bool flag = base.Configuration.Duties.DutyModeOverrides.TryGetValue(num, out value3);
EDutyMode num2 = (flag ? value3 : ((EDutyMode)(-1)));
Name = "Use Default";
string[] dutyModeLabels = DutyModeLabels;
int num3 = 0;
string[] array = new string[1 + dutyModeLabels.Length];
array[num3] = Name;
num3++;
ReadOnlySpan<string> readOnlySpan = new ReadOnlySpan<string>(dutyModeLabels);
readOnlySpan.CopyTo(new Span<string>(array).Slice(num3, readOnlySpan.Length));
num3 += readOnlySpan.Length;
string[] items2 = array;
int currentItem2 = (int)(num2 + 1);
ImGui.SetNextItemWidth(150f);
if (ImGui.Combo(string.Empty, ref currentItem2, in items2, items2.Length))
{
if (currentItem2 == 0)
{
base.Configuration.Duties.DutyModeOverrides.Remove(num);
}
else
{
base.Configuration.Duties.DutyModeOverrides[num] = (EDutyMode)(currentItem2 - 1);
}
Save();
}
if (ImGui.IsItemHovered())
{
EDutyMode eDutyMode = (flag ? value3 : base.Configuration.Duties.DefaultDutyMode);
ImGui.SetTooltip(flag ? ("Override: " + DutyModeLabels[(int)eDutyMode]) : ("Using default: " + DutyModeLabels[(int)eDutyMode]));
}
}
}
}
else if (base.Configuration.Duties.ExpansionHeaderStates.GetValueOrDefault(key, defaultValue: false))
{
base.Configuration.Duties.ExpansionHeaderStates[key] = false;
Save();
}
}
}
private (int enabledCount, int totalCount) GetDutyCountsForExpansion(EExpansionVersion expansion)
{
if (!_contentFinderConditionNames.TryGetValue(expansion, out List<DutyInfo> value))
{
return (enabledCount: 0, totalCount: 0);
}
int num = 0;
int num2 = 0;
foreach (var (num5, _, _) in value)
{
if (_questRegistry.TryGetDutyByContentFinderConditionId(num5, out DutyOptions dutyOptions))
{
num2++;
if (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(num5) || (!base.Configuration.Duties.BlacklistedDutyCfcIds.Contains(num5) && dutyOptions.Enabled))
{
num++;
}
}
}
return (enabledCount: num, totalCount: num2);
}
private void DrawEnableAllButton()
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.CheckCircle, "Enable All"))
{
base.Configuration.Duties.BlacklistedDutyCfcIds.Clear();
base.Configuration.Duties.WhitelistedDutyCfcIds.Clear();
foreach (List<DutyInfo> value in _contentFinderConditionNames.Values)
{
foreach (var (num3, _, _) in value)
{
if (_questRegistry.TryGetDutyByContentFinderConditionId(num3, out DutyOptions _))
{
base.Configuration.Duties.WhitelistedDutyCfcIds.Add(num3);
}
}
}
Save();
}
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Enable all of the duties, use at your own risk.");
}
}
private void DrawClipboardButtons()
{
using (ImRaii.Disabled(base.Configuration.Duties.WhitelistedDutyCfcIds.Count + base.Configuration.Duties.BlacklistedDutyCfcIds.Count + base.Configuration.Duties.DutyModeOverrides.Count == 0))
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Copy, "Export to clipboard"))
{
IEnumerable<string> first = base.Configuration.Duties.WhitelistedDutyCfcIds.Select((uint x) => $"{"+"}{x}");
IEnumerable<string> second = base.Configuration.Duties.BlacklistedDutyCfcIds.Select((uint x) => $"{"-"}{x}");
IEnumerable<string> second2 = base.Configuration.Duties.DutyModeOverrides.Select((KeyValuePair<uint, EDutyMode> x) => $"{"M:"}{x.Key}:{x.Value}");
ImGui.SetClipboardText("qst:duty:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(";", first.Concat(second).Concat(second2)))));
}
}
ImGui.SameLine();
string text = ImGui.GetClipboardText().Trim();
using (ImRaii.Disabled(string.IsNullOrEmpty(text) || !text.StartsWith("qst:duty:", StringComparison.InvariantCulture)))
{
if (!ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Paste, "Import from Clipboard"))
{
return;
}
text = text.Substring("qst:duty:".Length);
string text2 = Encoding.UTF8.GetString(Convert.FromBase64String(text));
base.Configuration.Duties.WhitelistedDutyCfcIds.Clear();
base.Configuration.Duties.BlacklistedDutyCfcIds.Clear();
base.Configuration.Duties.DutyModeOverrides.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.Duties.WhitelistedDutyCfcIds.Add(result);
}
if (text3.StartsWith("-", StringComparison.InvariantCulture) && uint.TryParse(text3.AsSpan("-".Length), CultureInfo.InvariantCulture, out var result2))
{
base.Configuration.Duties.BlacklistedDutyCfcIds.Add(result2);
}
if (!text3.StartsWith("M:", StringComparison.InvariantCulture))
{
continue;
}
ReadOnlySpan<char> span = text3.AsSpan("M:".Length);
int num2 = span.IndexOf(':');
if (num2 > 0 && uint.TryParse(span.Slice(0, num2), CultureInfo.InvariantCulture, out var result3))
{
int num3 = num2 + 1;
if (int.TryParse(span.Slice(num3, span.Length - num3), CultureInfo.InvariantCulture, out var result4) && Enum.IsDefined(typeof(EDutyMode), result4))
{
base.Configuration.Duties.DutyModeOverrides[result3] = (EDutyMode)result4;
}
}
}
Save();
}
}
private void DrawResetButton()
{
using (ImRaii.Disabled(!ImGui.IsKeyDown(ImGuiKey.ModCtrl)))
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Undo, "Reset to default"))
{
base.Configuration.Duties.WhitelistedDutyCfcIds.Clear();
base.Configuration.Duties.BlacklistedDutyCfcIds.Clear();
base.Configuration.Duties.DutyModeOverrides.Clear();
base.Configuration.Duties.DefaultDutyMode = EDutyMode.Support;
Save();
}
}
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
ImGui.SetTooltip("Hold CTRL to enable this button.");
}
}
}