407 lines
16 KiB
C#
407 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Plugin;
|
|
using Dalamud.Plugin.Services;
|
|
using Dalamud.Utility;
|
|
using Lumina.Excel;
|
|
using Lumina.Excel.Sheets;
|
|
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, int Level);
|
|
|
|
private const string DutyClipboardPrefix = "qst:duty:";
|
|
|
|
private const uint ContentTypeDungeons = 2u;
|
|
|
|
private const uint ContentTypeTrials = 4u;
|
|
|
|
private const uint ContentTypeRaids = 5u;
|
|
|
|
private const uint MemberTypeNormalRaid = 3u;
|
|
|
|
private const uint MemberTypeAllianceRaid = 4u;
|
|
|
|
private static readonly string[] DutyModeLabels = new string[3] { "Duty Support", "Unsync (Solo)", "Unsync (Party)" };
|
|
|
|
private static readonly string[] DutyTabLabels = new string[4] { "Dungeons", "Trials", "Normal Raids", "Alliance Raids" };
|
|
|
|
private readonly AutoDutyIpc _autoDutyIpc;
|
|
|
|
private readonly Dictionary<EExpansionVersion, List<DutyInfo>> _contentFinderConditionNames;
|
|
|
|
private readonly Dictionary<EExpansionVersion, List<DutyInfo>> _allTrialNames;
|
|
|
|
private readonly Dictionary<EExpansionVersion, List<DutyInfo>> _allNormalRaidNames;
|
|
|
|
private readonly Dictionary<EExpansionVersion, List<DutyInfo>> _allAllianceRaidNames;
|
|
|
|
private int _selectedDutyTab;
|
|
|
|
public DutyConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, IDataManager dataManager, AutoDutyIpc autoDutyIpc, TerritoryData territoryData)
|
|
: base(pluginInterface, configuration)
|
|
{
|
|
_autoDutyIpc = autoDutyIpc;
|
|
ExcelSheet<ContentFinderCondition> excelSheet = dataManager.GetExcelSheet<ContentFinderCondition>();
|
|
_contentFinderConditionNames = LoadContentByType(excelSheet, territoryData, 2u);
|
|
_allTrialNames = LoadContentByType(excelSheet, territoryData, 4u);
|
|
_allNormalRaidNames = LoadContentByType(excelSheet, territoryData, 5u, 3u);
|
|
_allAllianceRaidNames = LoadContentByType(excelSheet, territoryData, 5u, 4u);
|
|
}
|
|
|
|
private static Dictionary<EExpansionVersion, List<DutyInfo>> LoadContentByType(ExcelSheet<ContentFinderCondition> sheet, TerritoryData territoryData, uint contentTypeId, uint? contentMemberTypeId = null)
|
|
{
|
|
IEnumerable<ContentFinderCondition> source = sheet.Where((ContentFinderCondition x) => x.RowId != 0 && x.Content.RowId != 0 && x.ContentType.RowId == contentTypeId);
|
|
if (contentMemberTypeId.HasValue)
|
|
{
|
|
source = source.Where((ContentFinderCondition x) => x.ContentMemberType.RowId == contentMemberTypeId.Value);
|
|
}
|
|
return (from x in source
|
|
select new
|
|
{
|
|
Expansion = (EExpansionVersion)x.TerritoryType.Value.ExVersion.RowId,
|
|
CfcId = x.RowId,
|
|
Name = (territoryData.GetContentFinderCondition(x.RowId)?.Name ?? x.Name.ToDalamudString().ToString()),
|
|
TerritoryId = x.TerritoryType.RowId,
|
|
Level = x.ClassJobLevelRequired,
|
|
SortKey = x.SortKey
|
|
} into x
|
|
orderby x.SortKey
|
|
group x by x.Expansion).ToDictionary(x => x.Key, x => x.Select(y => new DutyInfo(y.CfcId, y.TerritoryId, y.Name, y.Level)).ToList());
|
|
}
|
|
|
|
public void DrawAutoDutySettings()
|
|
{
|
|
bool value = base.Configuration.Duties.RunInstancedContentWithAutoDuty;
|
|
if (UiThemeUtils.WrappedCheckbox("Run instanced content with AutoDuty and BossMod", ref value, "The combat module used for this is configured by AutoDuty, ignoring whichever selection you've made in Questionable's \"General\" configuration.\n\nWork in Progress:\n• Duty Support mode is limited.\n• Unsynced modes require being significantly overleveled or having a high-level party member.\n• Most players will need to use Duty Support mode when progressing normally through the game."))
|
|
{
|
|
base.Configuration.Duties.RunInstancedContentWithAutoDuty = value;
|
|
Save();
|
|
}
|
|
using (ImRaii.Disabled(!value))
|
|
{
|
|
bool value2 = base.Configuration.Duties.RunLevelingModeWhenUnderleveled;
|
|
if (UiThemeUtils.WrappedCheckbox("Run AutoDuty Leveling mode when underleveled for MSQ", ref value2, "When enabled, Questionable will automatically run AutoDuty's Leveling mode when your character is underleveled for the next Main Scenario Quest.\n\nLeveling mode runs the highest available dungeon for your level to gain XP.\n\nThis is useful for characters without the Road to 90 XP buff who may not have enough XP to continue the MSQ."))
|
|
{
|
|
base.Configuration.Duties.RunLevelingModeWhenUnderleveled = value2;
|
|
Save();
|
|
}
|
|
bool value3 = base.Configuration.Duties.AutoUnsyncOverleveled;
|
|
if (UiThemeUtils.WrappedCheckbox("Run dungeons unsynced when safely overleveled", ref value3, "Runs 4-man dungeons solo as an unrestricted party instead of with Duty Support if you are at least 15 levels above the dungeon's level sync, or 10 levels above with an item level advantage of 100 or more.\n\nOnly applies when the default duty mode is Duty Support and the dungeon has no built-in or manually configured duty mode."))
|
|
{
|
|
base.Configuration.Duties.AutoUnsyncOverleveled = value3;
|
|
Save();
|
|
}
|
|
ImGui.Spacing();
|
|
ImGui.Text("Default duty mode:");
|
|
ImGui.SameLine();
|
|
int currentItem = (int)base.Configuration.Duties.DefaultDutyMode;
|
|
ImGui.SetNextItemWidth(MathF.Min(ImGui.GetContentRegionAvail().X * 0.6f, 260f));
|
|
if (UiThemeUtils.ThemedCombo("##DefaultDutyMode", ref currentItem, DutyModeLabels, DutyModeLabels.Length))
|
|
{
|
|
base.Configuration.Duties.DefaultDutyMode = (EDutyMode)currentItem;
|
|
Save();
|
|
}
|
|
ImGui.SameLine();
|
|
UiThemeUtils.InfoIcon("Duty Support: Run with NPC party members (level synced)\nUnsync (Solo): Run alone at your current level (unsynced)\nUnsync (Party): Run with your party at current level (unsynced)\n\nYou can override this setting for individual duties below.");
|
|
ImGui.Spacing();
|
|
UiThemeUtils.SubSectionHeader("AutoDuty loop mode (advanced)");
|
|
using (ImRaii.PushIndent())
|
|
{
|
|
bool value4 = base.Configuration.Advanced.DisableAutoDutyBareMode;
|
|
if (UiThemeUtils.WrappedCheckbox("Use Pre-Loop/Loop/Post-Loop settings", ref value4, "Typically, the loop settings for AutoDuty are disabled when running dungeons with Questionable, since they can cause issues (or even shut down your PC)."))
|
|
{
|
|
base.Configuration.Advanced.DisableAutoDutyBareMode = value4;
|
|
Save();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void DrawTab()
|
|
{
|
|
UiThemeUtils.SectionHeader("Duty Settings");
|
|
ImGui.Spacing();
|
|
var (contentStartPos, availableWidth, drawList) = UiThemeUtils.BeginCard();
|
|
DrawAutoDutySettings();
|
|
UiThemeUtils.EndCard(contentStartPos, availableWidth, drawList);
|
|
UiThemeUtils.SectionSpacing();
|
|
bool runInstancedContentWithAutoDuty = base.Configuration.Duties.RunInstancedContentWithAutoDuty;
|
|
using (ImRaii.Disabled(!runInstancedContentWithAutoDuty))
|
|
{
|
|
UiThemeUtils.SectionHeader("Duty Overrides");
|
|
ImGui.Spacing();
|
|
(Vector2 ContentStartPos, float AvailableWidth, ImDrawListPtr DrawList) tuple2 = UiThemeUtils.BeginCard();
|
|
Vector2 item = tuple2.ContentStartPos;
|
|
float item2 = tuple2.AvailableWidth;
|
|
ImDrawListPtr item3 = tuple2.DrawList;
|
|
Dictionary<EExpansionVersion, List<DutyInfo>> contentByExpansion = _selectedDutyTab switch
|
|
{
|
|
1 => _allTrialNames,
|
|
2 => _allNormalRaidNames,
|
|
3 => _allAllianceRaidNames,
|
|
_ => _contentFinderConditionNames,
|
|
};
|
|
_selectedDutyTab = UiThemeUtils.DrawTabBar(DutyTabLabels, _selectedDutyTab);
|
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().ItemSpacing.Y);
|
|
DrawConfigTable(runInstancedContentWithAutoDuty, contentByExpansion);
|
|
DrawEnableAllButton();
|
|
ImGui.SameLine();
|
|
DrawClipboardButtons("qst:duty:", base.Configuration.Duties.WhitelistedDutyCfcIds, base.Configuration.Duties.BlacklistedDutyCfcIds, base.Configuration.Duties.DutyModeOverrides);
|
|
ImGui.SameLine();
|
|
if (UiThemeUtils.DestructiveButton(FontAwesomeIcon.Undo, "Reset to defaults"))
|
|
{
|
|
base.Configuration.Duties.WhitelistedDutyCfcIds.Clear();
|
|
base.Configuration.Duties.BlacklistedDutyCfcIds.Clear();
|
|
base.Configuration.Duties.DutyModeOverrides.Clear();
|
|
base.Configuration.Duties.DefaultDutyMode = EDutyMode.Support;
|
|
Save();
|
|
}
|
|
UiThemeUtils.EndCard(item, item2, item3);
|
|
}
|
|
}
|
|
|
|
private void DrawConfigTable(bool runInstancedContentWithAutoDuty, Dictionary<EExpansionVersion, List<DutyInfo>> contentByExpansion)
|
|
{
|
|
Vector2 contentRegionAvail = ImGui.GetContentRegionAvail();
|
|
using ImRaii.ChildDisposable childDisposable = ImRaii.Child("DutyConfiguration", new Vector2(contentRegionAvail.X, MathF.Max(contentRegionAvail.Y - 30f - 12f, 200f)), border: true);
|
|
if (!childDisposable)
|
|
{
|
|
return;
|
|
}
|
|
Dictionary<string, bool> expansionHeaderStates = base.Configuration.Duties.ExpansionHeaderStates;
|
|
EExpansionVersion[] values = Enum.GetValues<EExpansionVersion>();
|
|
for (int i = 0; i < values.Length; i++)
|
|
{
|
|
EExpansionVersion eExpansionVersion = values[i];
|
|
(int enabledCount, int totalCount) dutyCountsForExpansion = GetDutyCountsForExpansion(eExpansionVersion, contentByExpansion);
|
|
int item = dutyCountsForExpansion.enabledCount;
|
|
int item2 = dutyCountsForExpansion.totalCount;
|
|
string text = eExpansionVersion.ToString();
|
|
bool open = expansionHeaderStates.GetValueOrDefault(text, defaultValue: false);
|
|
if (UiThemeUtils.ThemedAccordion(text, eExpansionVersion.ToFriendlyString(), (item2 > 0) ? $"{item}/{item2}" : null, ref open))
|
|
{
|
|
using ImRaii.TableDisposable tableDisposable = UiThemeUtils.BeginThemedTable($"Duties{eExpansionVersion}", 4, ImGuiTableFlags.SizingFixedFit);
|
|
if ((bool)tableDisposable)
|
|
{
|
|
ImGui.TableSetupColumn("Level", ImGuiTableColumnFlags.WidthFixed, 40f);
|
|
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupColumn("Enabled", ImGuiTableColumnFlags.WidthFixed, 180f);
|
|
ImGui.TableSetupColumn("Mode", ImGuiTableColumnFlags.WidthFixed, 140f);
|
|
if (contentByExpansion.TryGetValue(eExpansionVersion, out List<DutyInfo> value))
|
|
{
|
|
foreach (DutyInfo item3 in value)
|
|
{
|
|
DutyEntry value2;
|
|
bool flag = DutyRegistry.Duties.TryGetValue(item3.CfcId, out value2);
|
|
ImGui.TableNextRow();
|
|
string[] array;
|
|
int currentItem;
|
|
if (flag)
|
|
{
|
|
array = (value2.Enabled ? SupportedCfcOptions : UnsupportedCfcOptions);
|
|
currentItem = 0;
|
|
if (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(item3.CfcId))
|
|
{
|
|
currentItem = 1;
|
|
}
|
|
if (base.Configuration.Duties.BlacklistedDutyCfcIds.Contains(item3.CfcId))
|
|
{
|
|
currentItem = 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
array = new string[2] { "Disabled", "Enabled" };
|
|
currentItem = (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(item3.CfcId) ? 1 : 0);
|
|
}
|
|
if (ImGui.TableNextColumn())
|
|
{
|
|
ImGui.AlignTextToFramePadding();
|
|
if (item3.Level > 0)
|
|
{
|
|
ImU8String text2 = new ImU8String(2, 1);
|
|
text2.AppendLiteral("Lv");
|
|
text2.AppendFormatted(item3.Level);
|
|
ImGui.TextColored(in UiThemeUtils.AccentColor, text2);
|
|
}
|
|
}
|
|
if (ImGui.TableNextColumn())
|
|
{
|
|
ImGui.AlignTextToFramePadding();
|
|
float x;
|
|
using (ImRaii.PushFont(UiBuilder.IconFont))
|
|
{
|
|
x = ImGui.CalcTextSize(FontAwesomeIcon.InfoCircle.ToIconString()).X;
|
|
}
|
|
UiThemeUtils.RowLabel(item3.Name, x + ImGui.GetStyle().ItemSpacing.X);
|
|
if (runInstancedContentWithAutoDuty && !_autoDutyIpc.HasPath(item3.CfcId))
|
|
{
|
|
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("This duty is not supported by AutoDuty");
|
|
ImGui.PopTextWrapPos();
|
|
}
|
|
}
|
|
}
|
|
else if (flag && value2.Notes.Length != 0)
|
|
{
|
|
ConfigComponent.DrawNotes(value2.Enabled, value2.Notes);
|
|
}
|
|
}
|
|
if (ImGui.TableNextColumn())
|
|
{
|
|
ImU8String id = new ImU8String(16, 1);
|
|
id.AppendLiteral("##DungeonEnabled");
|
|
id.AppendFormatted(item3.CfcId);
|
|
using (ImRaii.PushId(id))
|
|
{
|
|
ImGui.SetNextItemWidth(-1f);
|
|
if (UiThemeUtils.ThemedCombo(string.Empty, ref currentItem, array, array.Length))
|
|
{
|
|
base.Configuration.Duties.WhitelistedDutyCfcIds.Remove(item3.CfcId);
|
|
base.Configuration.Duties.BlacklistedDutyCfcIds.Remove(item3.CfcId);
|
|
if (flag)
|
|
{
|
|
switch (currentItem)
|
|
{
|
|
case 1:
|
|
base.Configuration.Duties.WhitelistedDutyCfcIds.Add(item3.CfcId);
|
|
break;
|
|
case 2:
|
|
base.Configuration.Duties.BlacklistedDutyCfcIds.Add(item3.CfcId);
|
|
break;
|
|
}
|
|
}
|
|
else if (currentItem == 1)
|
|
{
|
|
base.Configuration.Duties.WhitelistedDutyCfcIds.Add(item3.CfcId);
|
|
}
|
|
Save();
|
|
}
|
|
}
|
|
}
|
|
if (!ImGui.TableNextColumn())
|
|
{
|
|
continue;
|
|
}
|
|
ImU8String id2 = new ImU8String(13, 1);
|
|
id2.AppendLiteral("##DungeonMode");
|
|
id2.AppendFormatted(item3.CfcId);
|
|
using (ImRaii.PushId(id2))
|
|
{
|
|
EDutyMode value3;
|
|
bool flag2 = base.Configuration.Duties.DutyModeOverrides.TryGetValue(item3.CfcId, out value3);
|
|
EDutyMode num = (flag2 ? value3 : ((EDutyMode)(-1)));
|
|
string text3 = "Use Default";
|
|
string[] dutyModeLabels = DutyModeLabels;
|
|
int num2 = 0;
|
|
string[] array2 = new string[1 + dutyModeLabels.Length];
|
|
array2[num2] = text3;
|
|
num2++;
|
|
ReadOnlySpan<string> readOnlySpan = new ReadOnlySpan<string>(dutyModeLabels);
|
|
readOnlySpan.CopyTo(new Span<string>(array2).Slice(num2, readOnlySpan.Length));
|
|
num2 += readOnlySpan.Length;
|
|
string[] array3 = array2;
|
|
int currentItem2 = (int)(num + 1);
|
|
ImGui.SetNextItemWidth(-1f);
|
|
if (UiThemeUtils.ThemedCombo(string.Empty, ref currentItem2, array3, array3.Length))
|
|
{
|
|
if (currentItem2 == 0)
|
|
{
|
|
base.Configuration.Duties.DutyModeOverrides.Remove(item3.CfcId);
|
|
}
|
|
else
|
|
{
|
|
base.Configuration.Duties.DutyModeOverrides[item3.CfcId] = (EDutyMode)(currentItem2 - 1);
|
|
}
|
|
Save();
|
|
}
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
EDutyMode eDutyMode = (flag2 ? value3 : base.Configuration.Duties.DefaultDutyMode);
|
|
ImGui.SetTooltip(flag2 ? ("Override: " + DutyModeLabels[(int)eDutyMode]) : ("Using default: " + DutyModeLabels[(int)eDutyMode]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (open != expansionHeaderStates.GetValueOrDefault(text, defaultValue: false))
|
|
{
|
|
expansionHeaderStates[text] = open;
|
|
Save();
|
|
}
|
|
}
|
|
}
|
|
|
|
private (int enabledCount, int totalCount) GetDutyCountsForExpansion(EExpansionVersion expansion, Dictionary<EExpansionVersion, List<DutyInfo>> contentByExpansion)
|
|
{
|
|
if (!contentByExpansion.TryGetValue(expansion, out List<DutyInfo> value))
|
|
{
|
|
return (enabledCount: 0, totalCount: 0);
|
|
}
|
|
int num = 0;
|
|
int num2 = 0;
|
|
foreach (DutyInfo item in value)
|
|
{
|
|
if (DutyRegistry.Duties.TryGetValue(item.CfcId, out DutyEntry value2))
|
|
{
|
|
num2++;
|
|
if (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(item.CfcId) || (!base.Configuration.Duties.BlacklistedDutyCfcIds.Contains(item.CfcId) && value2.Enabled))
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
num2++;
|
|
if (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(item.CfcId))
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
}
|
|
return (enabledCount: num, totalCount: num2);
|
|
}
|
|
|
|
private void DrawEnableAllButton()
|
|
{
|
|
if (UiThemeUtils.IconTextButton(FontAwesomeIcon.CheckCircle, "Enable All"))
|
|
{
|
|
base.Configuration.Duties.BlacklistedDutyCfcIds.Clear();
|
|
base.Configuration.Duties.WhitelistedDutyCfcIds.Clear();
|
|
foreach (List<DutyInfo> item in _contentFinderConditionNames.Values.Concat<List<DutyInfo>>(_allTrialNames.Values).Concat<List<DutyInfo>>(_allNormalRaidNames.Values).Concat<List<DutyInfo>>(_allAllianceRaidNames.Values))
|
|
{
|
|
foreach (DutyInfo item2 in item)
|
|
{
|
|
base.Configuration.Duties.WhitelistedDutyCfcIds.Add(item2.CfcId);
|
|
}
|
|
}
|
|
Save();
|
|
}
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.SetTooltip("Enable all of the duties, use at your own risk.");
|
|
}
|
|
}
|
|
}
|