punish v6.8.18.0

This commit is contained in:
alydev 2025-10-09 07:47:19 +10:00
commit e786325cda
322 changed files with 554232 additions and 0 deletions

View file

@ -0,0 +1,88 @@
using System.Collections.Generic;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Text;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
namespace Questionable.Windows.ConfigComponents;
internal abstract class ConfigComponent
{
protected const string DutyClipboardSeparator = ";";
protected const string DutyWhitelistPrefix = "+";
protected const string DutyBlacklistPrefix = "-";
protected readonly string[] SupportedCfcOptions = new string[3]
{
$"{SeIconChar.Circle.ToIconChar()} Enabled (Default)",
$"{SeIconChar.Circle.ToIconChar()} Enabled",
$"{SeIconChar.Cross.ToIconChar()} Disabled"
};
protected readonly string[] UnsupportedCfcOptions = new string[3]
{
$"{SeIconChar.Cross.ToIconChar()} Disabled (Default)",
$"{SeIconChar.Circle.ToIconChar()} Enabled",
$"{SeIconChar.Cross.ToIconChar()} Disabled"
};
private readonly IDalamudPluginInterface _pluginInterface;
protected Configuration Configuration { get; }
protected ConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration)
{
_pluginInterface = pluginInterface;
Configuration = configuration;
}
public abstract void DrawTab();
protected void Save()
{
_pluginInterface.SavePluginConfig(Configuration);
}
protected static string FormatLevel(int level, bool includePrefix = true)
{
if (level == 0)
{
return string.Empty;
}
return $"{(includePrefix ? SeIconChar.LevelEn.ToIconString() : string.Empty)}{FormatLevel(level / 10, includePrefix: false)}{((SeIconChar)(57440 + level % 10)).ToIconChar()}";
}
protected static void DrawNotes(bool enabledByDefault, IEnumerable<string> notes)
{
using ImRaii.Color color = new ImRaii.Color();
color.Push(ImGuiCol.TextDisabled, (!enabledByDefault) ? ImGuiColors.DalamudYellow : ImGuiColors.ParsedBlue);
ImGui.SameLine();
using (ImRaii.PushFont(UiBuilder.IconFont))
{
if (!enabledByDefault)
{
ImGui.TextDisabled(FontAwesomeIcon.ExclamationTriangle.ToIconString());
}
else
{
ImGui.TextDisabled(FontAwesomeIcon.InfoCircle.ToIconString());
}
}
if (!ImGui.IsItemHovered())
{
return;
}
using (ImRaii.Tooltip())
{
ImGui.TextColored(ImGuiColors.DalamudYellow, "While testing, the following issues have been found:");
foreach (string note in notes)
{
ImGui.BulletText(note);
}
}
}
}

View file

@ -0,0 +1,131 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
namespace Questionable.Windows.ConfigComponents;
internal sealed class DebugConfigComponent : ConfigComponent
{
public DebugConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration)
: base(pluginInterface, configuration)
{
}
public override void DrawTab()
{
using ImRaii.IEndObject endObject = ImRaii.TabItem("Advanced###Debug");
if (!endObject)
{
return;
}
ImGui.TextColored(ImGuiColors.DalamudRed, "Enabling any option here may cause unexpected behavior. Use at your own risk.");
ImGui.Separator();
bool v = base.Configuration.Advanced.DebugOverlay;
if (ImGui.Checkbox("Enable debug overlay", ref v))
{
base.Configuration.Advanced.DebugOverlay = v;
Save();
}
using (ImRaii.Disabled(!v))
{
using (ImRaii.PushIndent())
{
bool v2 = base.Configuration.Advanced.CombatDataOverlay;
if (ImGui.Checkbox("Enable combat data overlay", ref v2))
{
base.Configuration.Advanced.CombatDataOverlay = v2;
Save();
}
}
}
bool v3 = base.Configuration.Advanced.NeverFly;
if (ImGui.Checkbox("Disable flying (even if unlocked for the zone)", ref v3))
{
base.Configuration.Advanced.NeverFly = v3;
Save();
}
bool v4 = base.Configuration.Advanced.AdditionalStatusInformation;
if (ImGui.Checkbox("Draw additional status information", ref v4))
{
base.Configuration.Advanced.AdditionalStatusInformation = v4;
Save();
}
ImGui.Separator();
ImGui.Text("AutoDuty Settings");
using (ImRaii.PushIndent())
{
ImGui.AlignTextToFramePadding();
bool v5 = base.Configuration.Advanced.DisableAutoDutyBareMode;
if (ImGui.Checkbox("Use Pre-Loop/Loop/Post-Loop settings", ref v5))
{
base.Configuration.Advanced.DisableAutoDutyBareMode = v5;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("Typically, the loop settings for AutoDuty are disabled when running dungeons with Questionable, since they can cause issues (or even shut down your PC).");
}
ImGui.Separator();
ImGui.Text("Quest/Interaction Skips");
using (ImRaii.PushIndent())
{
bool v6 = base.Configuration.Advanced.SkipAetherCurrents;
if (ImGui.Checkbox("Don't pick up aether currents/aether current quests", ref v6))
{
base.Configuration.Advanced.SkipAetherCurrents = v6;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("If not done during the MSQ by Questionable, you have to manually pick up any missed aether currents/quests. There is no way to automatically pick up all missing aether currents.");
bool v7 = base.Configuration.Advanced.SkipClassJobQuests;
if (ImGui.Checkbox("Don't pick up class/job/role quests", ref v7))
{
base.Configuration.Advanced.SkipClassJobQuests = v7;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("Class and job skills for A Realm Reborn, Heavensward and (for the Lv70 skills) Stormblood are locked behind quests. Not recommended if you plan on queueing for instances with duty finder/party finder.");
bool v8 = base.Configuration.Advanced.SkipARealmRebornHardModePrimals;
if (ImGui.Checkbox("Don't pick up ARR hard mode primal quests", ref v8))
{
base.Configuration.Advanced.SkipARealmRebornHardModePrimals = v8;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("Hard mode Ifrit/Garuda/Titan are required for the Patch 2.5 quest 'Good Intentions' and to start Heavensward.");
bool v9 = base.Configuration.Advanced.SkipCrystalTowerRaids;
if (ImGui.Checkbox("Don't pick up Crystal Tower quests", ref v9))
{
base.Configuration.Advanced.SkipCrystalTowerRaids = v9;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("Crystal Tower raids are required for the Patch 2.55 quest 'A Time to Every Purpose' and to start Heavensward.");
bool v10 = base.Configuration.Advanced.PreventQuestCompletion;
if (ImGui.Checkbox("Prevent quest completion", ref v10))
{
base.Configuration.Advanced.PreventQuestCompletion = v10;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("When enabled, Questionable will not attempt to turn-in and complete quests. This will do everything automatically except the final turn-in step.");
bool v11 = base.Configuration.Advanced.ShowWindowOnStart;
if (ImGui.Checkbox("Show window on start", ref v11))
{
base.Configuration.Advanced.ShowWindowOnStart = v11;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("When enabled, Questionable's progress window will show when the plugin is loaded.");
bool v12 = base.Configuration.Advanced.StartMinimized;
if (ImGui.Checkbox("Start minimized", ref v12))
{
base.Configuration.Advanced.StartMinimized = v12;
Save();
}
ImGui.SameLine();
ImGuiComponents.HelpMarker("When enabled, Questionable's progress window will be in its minimized state when loaded.");
}
}
}

View file

@ -0,0 +1,316 @@
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 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.");
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(650f, 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, 2, ImGuiTableFlags.SizingFixedFit);
if (!ImRaii.IEndObject.op_True(endObject2))
{
continue;
}
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("Options", ImGuiTableColumnFlags.WidthFixed, 200f);
if (!_contentFinderConditionNames.TryGetValue(eExpansionVersion, out List<DutyInfo> value))
{
continue;
}
foreach (var (num3, value2, text2) in value)
{
if (!_questRegistry.TryGetDutyByContentFinderConditionId(num3, out DutyOptions dutyOptions))
{
continue;
}
ImGui.TableNextRow();
string[] items = (dutyOptions.Enabled ? SupportedCfcOptions : UnsupportedCfcOptions);
int currentItem = 0;
if (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(num3))
{
currentItem = 1;
}
if (base.Configuration.Duties.BlacklistedDutyCfcIds.Contains(num3))
{
currentItem = 2;
}
if (ImGui.TableNextColumn())
{
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted(text2);
if (ImGui.IsItemHovered() && base.Configuration.Advanced.AdditionalStatusInformation)
{
using ImRaii.IEndObject endObject3 = ImRaii.Tooltip();
if (endObject3)
{
ImGui.TextUnformatted(text2);
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(num3);
ImGui.BulletText(table);
}
}
if (runInstancedContentWithAutoDuty && !_autoDutyIpc.HasPath(num3))
{
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())
{
continue;
}
table = new ImU8String(9, 1);
table.AppendLiteral("##Dungeon");
table.AppendFormatted(num3);
using (ImRaii.PushId(table))
{
ImGui.SetNextItemWidth(200f);
if (ImGui.Combo(string.Empty, ref currentItem, in items, items.Length))
{
base.Configuration.Duties.WhitelistedDutyCfcIds.Remove(num3);
base.Configuration.Duties.BlacklistedDutyCfcIds.Remove(num3);
switch (currentItem)
{
case 1:
base.Configuration.Duties.WhitelistedDutyCfcIds.Add(num3);
break;
case 2:
base.Configuration.Duties.BlacklistedDutyCfcIds.Add(num3);
break;
}
Save();
}
}
}
}
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 == 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}");
ImGui.SetClipboardText("qst:duty:" + 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: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();
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);
}
}
}
}
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();
Save();
}
}
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
ImGui.SetTooltip("Hold CTRL to enable this button.");
}
}
}

View file

@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
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;
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: x.Singular.ToString()) into x
where !string.IsNullOrEmpty(x.Name)
orderby x.Name
select x).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();
}
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 currentItem2 = Array.FindIndex(_mountIds, (uint x) => x == base.Configuration.General.MountId);
if (currentItem2 == -1)
{
currentItem2 = 0;
base.Configuration.General.MountId = _mountIds[currentItem2];
Save();
}
if (ImGui.Combo("Preferred Mount", ref currentItem2, in _mountNames, _mountNames.Length))
{
base.Configuration.General.MountId = _mountIds[currentItem2];
Save();
}
int currentItem3 = (int)base.Configuration.General.GrandCompany;
if (ImGui.Combo("Preferred Grand Company", ref currentItem3, in _grandCompanyNames, _grandCompanyNames.Length))
{
base.Configuration.General.GrandCompany = (FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany)currentItem3;
Save();
}
int currentItem4 = Array.IndexOf(_classJobIds, base.Configuration.General.CombatJob);
if (currentItem4 == -1)
{
base.Configuration.General.CombatJob = EClassJob.Adventurer;
Save();
currentItem4 = 0;
}
if (ImGui.Combo("Preferred Combat Job", ref currentItem4, in _classJobNames, _classJobNames.Length))
{
base.Configuration.General.CombatJob = _classJobIds[currentItem4];
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("Use 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();
}
}
ImGui.Separator();
ImGui.Text("Questing");
using (ImRaii.PushIndent())
{
bool v4 = base.Configuration.General.ConfigureTextAdvance;
if (ImGui.Checkbox("Automatically configure TextAdvance with the recommended settings", ref v4))
{
base.Configuration.General.ConfigureTextAdvance = v4;
Save();
}
bool v5 = base.Configuration.General.SkipLowPriorityDuties;
if (ImGui.Checkbox("Unlock certain optional dungeons and raids (instead of waiting for completion)", ref v5))
{
base.Configuration.General.SkipLowPriorityDuties = v5;
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 text = new ImU8String(0, 1);
text.AppendFormatted(contentFinderConditionData.Name);
ImGui.BulletText(text);
}
}
}
}
ImGui.Spacing();
bool v6 = base.Configuration.General.AutoStepRefreshEnabled;
if (ImGui.Checkbox("Automatically refresh quest steps when stuck (WIP see tooltip)", ref v6))
{
base.Configuration.General.AutoStepRefreshEnabled = v6;
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.");
ImGui.Text("WIP feature, rather than remove it, this is a warning that it isn't fully complete.");
}
}
using (ImRaii.Disabled(!v6))
{
ImGui.Indent();
int v7 = base.Configuration.General.AutoStepRefreshDelaySeconds;
ImGui.SetNextItemWidth(150f);
if (ImGui.SliderInt("Refresh delay (seconds)", ref v7, 10, 180))
{
base.Configuration.General.AutoStepRefreshDelaySeconds = v7;
Save();
}
Vector4 col = new Vector4(0.7f, 0.7f, 0.7f, 1f);
ImU8String text = new ImU8String(77, 1);
text.AppendLiteral("Quest steps will refresh automatically after ");
text.AppendFormatted(v7);
text.AppendLiteral(" seconds if no progress is made.");
ImGui.TextColored(in col, text);
ImGui.Unindent();
}
}
}
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;
}
}

View file

@ -0,0 +1,72 @@
using System;
using System.Linq;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Text;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
using Dalamud.Utility;
using Questionable.External;
namespace Questionable.Windows.ConfigComponents;
internal sealed class NotificationConfigComponent : ConfigComponent
{
private readonly NotificationMasterIpc _notificationMasterIpc;
public NotificationConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, NotificationMasterIpc notificationMasterIpc)
: base(pluginInterface, configuration)
{
_notificationMasterIpc = notificationMasterIpc;
}
public override void DrawTab()
{
using ImRaii.IEndObject endObject = ImRaii.TabItem("Notifications###Notifications");
if (!endObject)
{
return;
}
bool v = base.Configuration.Notifications.Enabled;
if (ImGui.Checkbox("Enable notifications when manual interaction is required", ref v))
{
base.Configuration.Notifications.Enabled = v;
Save();
}
using (ImRaii.Disabled(!base.Configuration.Notifications.Enabled))
{
using (ImRaii.PushIndent())
{
XivChatType[] array = (from x in Enum.GetValues<XivChatType>()
where x != XivChatType.StandardEmote
select x).ToArray();
int currentItem = Array.IndexOf(array, base.Configuration.Notifications.ChatType);
string[] items = array.Select((XivChatType t) => t.GetAttribute<XivChatTypeInfoAttribute>()?.FancyName ?? t.ToString()).ToArray();
if (ImGui.Combo("Chat channel", ref currentItem, in items, items.Length))
{
base.Configuration.Notifications.ChatType = array[currentItem];
Save();
}
ImGui.Separator();
ImGui.Text("NotificationMaster settings");
ImGui.SameLine();
ImGuiComponents.HelpMarker("Requires the plugin 'NotificationMaster' to be installed.");
using (ImRaii.Disabled(!_notificationMasterIpc.Enabled))
{
bool v2 = base.Configuration.Notifications.ShowTrayMessage;
if (ImGui.Checkbox("Show tray notification", ref v2))
{
base.Configuration.Notifications.ShowTrayMessage = v2;
Save();
}
bool v3 = base.Configuration.Notifications.FlashTaskbar;
if (ImGui.Checkbox("Flash taskbar icon", ref v3))
{
base.Configuration.Notifications.FlashTaskbar = v3;
Save();
}
}
}
}
}
}

View file

@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
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 Questionable.Controller;
using Questionable.External;
namespace Questionable.Windows.ConfigComponents;
internal sealed class PluginConfigComponent : ConfigComponent
{
private sealed record PluginInfo(string DisplayName, string InternalName, string Details, Uri WebsiteUri, Uri? DalamudRepositoryUri, string? ConfigCommand = null, List<PluginDetailInfo>? DetailsToCheck = null);
private sealed record PluginDetailInfo(string DisplayName, string Details, Func<bool> Predicate);
private static readonly IReadOnlyList<PluginInfo> RequiredPlugins = new global::_003C_003Ez__ReadOnlyArray<PluginInfo>(new PluginInfo[3]
{
new PluginInfo("vnavmesh", "vnavmesh", "vnavmesh handles the navigation within a zone, moving\nyour character to the next quest-related objective.", new Uri("https://github.com/awgil/ffxiv_navmesh/"), new Uri("https://puni.sh/api/repository/veyn")),
new PluginInfo("Lifestream", "Lifestream", "Used to travel to aethernet shards in cities.", new Uri("https://github.com/NightmareXIV/Lifestream"), new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")),
new PluginInfo("TextAdvance", "TextAdvance", "Automatically accepts and turns in quests, skips cutscenes\nand dialogue.", new Uri("https://github.com/NightmareXIV/TextAdvance"), new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json"))
});
private static readonly ReadOnlyDictionary<Configuration.ECombatModule, PluginInfo> CombatPlugins = new Dictionary<Configuration.ECombatModule, PluginInfo>
{
{
Questionable.Configuration.ECombatModule.BossMod,
new PluginInfo("Boss Mod (VBM)", "BossMod", string.Empty, new Uri("https://github.com/awgil/ffxiv_bossmod"), new Uri("https://puni.sh/api/repository/veyn"))
},
{
Questionable.Configuration.ECombatModule.WrathCombo,
new PluginInfo("Wrath Combo", "WrathCombo", string.Empty, new Uri("https://github.com/PunishXIV/WrathCombo"), new Uri("https://puni.sh/api/plugins"))
},
{
Questionable.Configuration.ECombatModule.RotationSolverReborn,
new PluginInfo("Rotation Solver Reborn", "RotationSolver", string.Empty, new Uri("https://github.com/FFXIV-CombatReborn/RotationSolverReborn"), new Uri("https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json"))
}
}.AsReadOnly();
private readonly IReadOnlyList<PluginInfo> _recommendedPlugins;
private readonly Configuration _configuration;
private readonly CombatController _combatController;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly UiUtils _uiUtils;
private readonly ICommandManager _commandManager;
public PluginConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, CombatController combatController, UiUtils uiUtils, ICommandManager commandManager, AutomatonIpc automatonIpc, PandorasBoxIpc pandorasBoxIpc)
: base(pluginInterface, configuration)
{
_configuration = configuration;
_combatController = combatController;
_pluginInterface = pluginInterface;
_uiUtils = uiUtils;
_commandManager = commandManager;
PluginInfo[] array = new PluginInfo[4];
Uri websiteUri = new Uri("https://github.com/Jaksuhn/Automaton");
Uri dalamudRepositoryUri = new Uri("https://puni.sh/api/repository/croizat");
int num = 1;
List<PluginDetailInfo> list = new List<PluginDetailInfo>(num);
CollectionsMarshal.SetCount(list, num);
Span<PluginDetailInfo> span = CollectionsMarshal.AsSpan(list);
int index = 0;
span[index] = new PluginDetailInfo("'Sniper no sniping' enabled", "Automatically completes sniping tasks introduced in Stormblood", () => automatonIpc.IsAutoSnipeEnabled);
array[0] = new PluginInfo("CBT (formerly known as Automaton)", "Automaton", "Automaton is a collection of automation-related tweaks.", websiteUri, dalamudRepositoryUri, "/cbt", list);
Uri websiteUri2 = new Uri("https://github.com/PunishXIV/PandorasBox");
Uri dalamudRepositoryUri2 = new Uri("https://puni.sh/api/plugins");
index = 1;
List<PluginDetailInfo> list2 = new List<PluginDetailInfo>(index);
CollectionsMarshal.SetCount(list2, index);
span = CollectionsMarshal.AsSpan(list2);
num = 0;
span[num] = new PluginDetailInfo("'Auto Active Time Maneuver' enabled", "Automatically completes active time maneuvers in\nsingle player instances, trials and raids\"", () => pandorasBoxIpc.IsAutoActiveTimeManeuverEnabled);
array[1] = new PluginInfo("Pandora's Box", "PandorasBox", "Pandora's Box is a collection of tweaks.", websiteUri2, dalamudRepositoryUri2, "/pandora", list2);
array[2] = new PluginInfo("NotificationMaster", "NotificationMaster", "Sends a configurable out-of-game notification if a quest\nrequires manual actions.", new Uri("https://github.com/NightmareXIV/NotificationMaster"), null);
array[3] = new PluginInfo("Artisan", "Artisan", "Automates crafting", new Uri("https://github.com/PunishXIV/Artisan"), new Uri("https://puni.sh/api/plugins"), "/artisan");
_recommendedPlugins = new global::_003C_003Ez__ReadOnlyArray<PluginInfo>(array);
}
public override void DrawTab()
{
using ImRaii.IEndObject endObject = ImRaii.TabItem("Dependencies###Plugins");
if (!(!endObject))
{
Draw(out var allRequiredInstalled);
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();
if (allRequiredInstalled)
{
ImGui.TextColored(ImGuiColors.ParsedGreen, "All required plugins are installed.");
}
else
{
ImGui.TextColored(ImGuiColors.DalamudRed, "Required plugins are missing, Questionable will not work properly.");
}
}
}
public void Draw(out bool allRequiredInstalled)
{
float checklistPadding;
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
checklistPadding = ImGui.CalcTextSize(FontAwesomeIcon.Check.ToIconString()).X + ImGui.GetStyle().ItemSpacing.X;
}
ImGui.Text("Questionable requires the following plugins to work:");
allRequiredInstalled = true;
using (ImRaii.PushIndent())
{
foreach (PluginInfo requiredPlugin in RequiredPlugins)
{
allRequiredInstalled &= DrawPlugin(requiredPlugin, checklistPadding);
}
}
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();
ImGui.Text("Questionable recommends Boss Mod (VBM) for rotation/combat automation.");
using (ImRaii.Disabled(_combatController.IsRunning))
{
using (ImRaii.PushIndent())
{
if (ImGui.RadioButton("No rotation/combat plugin (combat must be done manually)", _configuration.General.CombatModule == Questionable.Configuration.ECombatModule.None))
{
_configuration.General.CombatModule = Questionable.Configuration.ECombatModule.None;
_pluginInterface.SavePluginConfig(_configuration);
}
allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.BossMod, checklistPadding);
allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.WrathCombo, checklistPadding);
}
ImGui.Text("The following rotation/combat plugin(s) are provided for compatibility and testing purposes:");
using (ImRaii.PushIndent())
{
allRequiredInstalled &= DrawCombatPlugin(Questionable.Configuration.ECombatModule.RotationSolverReborn, checklistPadding);
}
}
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();
ImGui.Text("The following plugins are recommended, but not required:");
using (ImRaii.PushIndent())
{
foreach (PluginInfo recommendedPlugin in _recommendedPlugins)
{
DrawPlugin(recommendedPlugin, checklistPadding);
}
}
}
private bool DrawPlugin(PluginInfo plugin, float checklistPadding)
{
using (ImRaii.PushId("plugin_" + plugin.DisplayName))
{
IExposedPlugin exposedPlugin = FindInstalledPlugin(plugin);
bool flag = exposedPlugin != null;
string text = plugin.DisplayName;
if (exposedPlugin != null)
{
text += $" v{exposedPlugin.Version}";
}
_uiUtils.ChecklistItem(text, flag);
DrawPluginDetails(plugin, checklistPadding, flag);
return flag;
}
}
private bool DrawCombatPlugin(Configuration.ECombatModule combatModule, float checklistPadding)
{
ImGui.Spacing();
PluginInfo pluginInfo = CombatPlugins[combatModule];
using (ImRaii.PushId("plugin_" + pluginInfo.DisplayName))
{
IExposedPlugin exposedPlugin = FindInstalledPlugin(pluginInfo);
bool flag = exposedPlugin != null;
string text = pluginInfo.DisplayName;
if (exposedPlugin != null)
{
text += $" v{exposedPlugin.Version}";
}
if (ImGui.RadioButton(text, _configuration.General.CombatModule == combatModule))
{
_configuration.General.CombatModule = combatModule;
_pluginInterface.SavePluginConfig(_configuration);
}
ImGui.SameLine(0f);
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
Vector4 col = (flag ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed);
FontAwesomeIcon icon = (flag ? FontAwesomeIcon.Check : FontAwesomeIcon.Times);
ImGui.AlignTextToFramePadding();
ImGui.TextColored(in col, icon.ToIconString());
}
DrawPluginDetails(pluginInfo, checklistPadding, flag);
return flag || _configuration.General.CombatModule != combatModule;
}
}
private void DrawPluginDetails(PluginInfo plugin, float checklistPadding, bool isInstalled)
{
using (ImRaii.PushIndent(checklistPadding))
{
if (!string.IsNullOrEmpty(plugin.Details))
{
ImGui.TextUnformatted(plugin.Details);
}
bool flag = true;
if (plugin.DetailsToCheck != null)
{
foreach (PluginDetailInfo item in plugin.DetailsToCheck)
{
bool flag2 = item.Predicate();
flag = flag && flag2;
_uiUtils.ChecklistItem(item.DisplayName, isInstalled && flag2);
if (!string.IsNullOrEmpty(item.Details))
{
using (ImRaii.PushIndent(checklistPadding))
{
ImGui.TextUnformatted(item.Details);
}
}
}
}
ImGui.Spacing();
if (isInstalled)
{
if (!flag && plugin.ConfigCommand != null && plugin.ConfigCommand.StartsWith('/') && ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Cog, "Open configuration"))
{
_commandManager.ProcessCommand(plugin.ConfigCommand);
}
return;
}
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Globe, "Open Website"))
{
Util.OpenLink(plugin.WebsiteUri.ToString());
}
ImGui.SameLine();
if (plugin.DalamudRepositoryUri != null)
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Code, "Open Repository"))
{
Util.OpenLink(plugin.DalamudRepositoryUri.ToString());
}
}
else
{
ImGui.AlignTextToFramePadding();
ImGuiComponents.HelpMarker("Available on official Dalamud Repository");
}
}
}
private IExposedPlugin? FindInstalledPlugin(PluginInfo pluginInfo)
{
return _pluginInterface.InstalledPlugins.FirstOrDefault((IExposedPlugin x) => x.InternalName == pluginInfo.InternalName && x.IsLoaded);
}
}

View file

@ -0,0 +1,916 @@
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<string> 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<SinglePlayerDutyConfigComponent> _logger;
private ImmutableDictionary<EAetheryteLocation, List<SinglePlayerDutyInfo>> _startingCityBattles = ImmutableDictionary<EAetheryteLocation, List<SinglePlayerDutyInfo>>.Empty;
private ImmutableDictionary<EExpansionVersion, List<SinglePlayerDutyInfo>> _mainScenarioBattles = ImmutableDictionary<EExpansionVersion, List<SinglePlayerDutyInfo>>.Empty;
private ImmutableDictionary<EClassJob, List<SinglePlayerDutyInfo>> _jobQuestBattles = ImmutableDictionary<EClassJob, List<SinglePlayerDutyInfo>>.Empty;
private ImmutableDictionary<EClassJob, List<SinglePlayerDutyInfo>> _roleQuestBattles = ImmutableDictionary<EClassJob, List<SinglePlayerDutyInfo>>.Empty;
private ImmutableList<SinglePlayerDutyInfo> _otherRoleQuestBattles = ImmutableList<SinglePlayerDutyInfo>.Empty;
private ImmutableList<(string Label, List<SinglePlayerDutyInfo>)> _otherQuestBattles = ImmutableList<(string, List<SinglePlayerDutyInfo>)>.Empty;
public SinglePlayerDutyConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, TerritoryData territoryData, QuestRegistry questRegistry, QuestData questData, IDataManager dataManager, ClassJobUtils classJobUtils, ILogger<SinglePlayerDutyConfigComponent> logger)
: base(pluginInterface, configuration)
{
_territoryData = territoryData;
_questRegistry = questRegistry;
_questData = questData;
_dataManager = dataManager;
_classJobUtils = classJobUtils;
_logger = logger;
}
public void Reload()
{
List<ElementId> list = (from x in _territoryData.GetAllQuestsWithQuestBattles()
group x by x.QuestId into x
where x.Count() > 1
select x.Key).ToList();
List<SinglePlayerDutyInfo> list2 = new List<SinglePlayerDutyInfo>();
Dictionary<EAetheryteLocation, List<SinglePlayerDutyInfo>> dictionary = new Dictionary<EAetheryteLocation, List<SinglePlayerDutyInfo>>
{
{
EAetheryteLocation.Limsa,
new List<SinglePlayerDutyInfo>()
},
{
EAetheryteLocation.Gridania,
new List<SinglePlayerDutyInfo>()
},
{
EAetheryteLocation.Uldah,
new List<SinglePlayerDutyInfo>()
}
};
List<SinglePlayerDutyInfo> list3 = new List<SinglePlayerDutyInfo>();
Dictionary<ElementId, EClassJob> dictionary2 = (from x in Enum.GetValues<EClassJob>()
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<EClassJob, List<SinglePlayerDutyInfo>> dictionary3 = dictionary2.Values.Distinct().ToDictionary((EClassJob x) => x, (EClassJob _) => new List<SinglePlayerDutyInfo>());
Dictionary<ElementId, List<EClassJob>> 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<ElementId, (ElementId QuestId, EClassJob ClassJob)> x) => x.Key, (IGrouping<ElementId, (ElementId QuestId, EClassJob ClassJob)> x) => x.Select(((ElementId QuestId, EClassJob ClassJob) y) => y.ClassJob).ToList());
Dictionary<EClassJob, List<SinglePlayerDutyInfo>> dictionary5 = RoleQuestCategories.ToDictionary<(EClassJob, string), EClassJob, List<SinglePlayerDutyInfo>>(((EClassJob ClassJob, string Name) x) => x.ClassJob, ((EClassJob ClassJob, string Name) _) => new List<SinglePlayerDutyInfo>());
List<SinglePlayerDutyInfo> list4 = new List<SinglePlayerDutyInfo>();
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<EClassJob> 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<EAetheryteLocation, List<SinglePlayerDutyInfo>> x) => x.Key, (KeyValuePair<EAetheryteLocation, List<SinglePlayerDutyInfo>> x) => x.Value.OrderBy((SinglePlayerDutyInfo y) => y.SortKey).ToList());
_mainScenarioBattles = (from x in list2
group x by x.Expansion).ToImmutableDictionary((IGrouping<EExpansionVersion, SinglePlayerDutyInfo> x) => x.Key, (IGrouping<EExpansionVersion, SinglePlayerDutyInfo> x) => (from y in x
orderby y.JournalGenreId, y.SortKey, y.Index
select y).ToList());
_jobQuestBattles = dictionary3.Where((KeyValuePair<EClassJob, List<SinglePlayerDutyInfo>> x) => x.Value.Count > 0).ToImmutableDictionary((KeyValuePair<EClassJob, List<SinglePlayerDutyInfo>> x) => x.Key, (KeyValuePair<EClassJob, List<SinglePlayerDutyInfo>> 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<EClassJob, List<SinglePlayerDutyInfo>> x) => x.Key, (KeyValuePair<EClassJob, List<SinglePlayerDutyInfo>> 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<JournalGenre>().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<EExpansionVersion>();
for (int i = 0; i < values.Length; i++)
{
EExpansionVersion eExpansionVersion = values[i];
if (!_mainScenarioBattles.TryGetValue(eExpansionVersion, out List<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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<string> first = base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Select((uint x) => $"{"+"}{x}");
IEnumerable<string> 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<SinglePlayerDutyInfo> GetAllEnabledSinglePlayerDuties()
{
return from x in _startingCityBattles.Values.SelectMany((List<SinglePlayerDutyInfo> x) => x).Concat(_mainScenarioBattles.Values.SelectMany((List<SinglePlayerDutyInfo> x) => x)).Concat(_jobQuestBattles.Values.SelectMany((List<SinglePlayerDutyInfo> x) => x))
.Concat(_roleQuestBattles.Values.SelectMany((List<SinglePlayerDutyInfo> x) => x))
.Concat(_otherRoleQuestBattles)
.Concat(_otherQuestBattles.SelectMany<(string, List<SinglePlayerDutyInfo>), SinglePlayerDutyInfo>(((string Label, List<SinglePlayerDutyInfo>) x) => x.Item2))
where x.Enabled
select x;
}
private (int enabledCount, int totalCount) GetQuestBattleCounts(IReadOnlyList<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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<SinglePlayerDutyInfo> 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;
}
}

View file

@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Questionable.Controller;
using Questionable.Functions;
using Questionable.Model;
using Questionable.Model.Questing;
using Questionable.Windows.QuestComponents;
using Questionable.Windows.Utils;
namespace Questionable.Windows.ConfigComponents;
internal sealed class StopConditionComponent : ConfigComponent
{
private readonly IDalamudPluginInterface _pluginInterface;
private readonly QuestSelector _questSelector;
private readonly QuestRegistry _questRegistry;
private readonly QuestTooltipComponent _questTooltipComponent;
private readonly UiUtils _uiUtils;
private readonly IClientState _clientState;
public StopConditionComponent(IDalamudPluginInterface pluginInterface, QuestSelector questSelector, QuestFunctions questFunctions, QuestRegistry questRegistry, QuestTooltipComponent questTooltipComponent, UiUtils uiUtils, IClientState clientState, Configuration configuration)
: base(pluginInterface, configuration)
{
StopConditionComponent stopConditionComponent = this;
_pluginInterface = pluginInterface;
_questSelector = questSelector;
_questRegistry = questRegistry;
_questTooltipComponent = questTooltipComponent;
_uiUtils = uiUtils;
_clientState = clientState;
_questSelector.SuggestionPredicate = (Quest quest) => configuration.Stop.QuestsToStopAfter.All((ElementId x) => x != quest.Id);
_questSelector.DefaultPredicate = (Quest quest) => quest.Info.IsMainScenarioQuest && questFunctions.IsQuestAccepted(quest.Id);
_questSelector.QuestSelected = delegate(Quest quest)
{
configuration.Stop.QuestsToStopAfter.Add(quest.Id);
stopConditionComponent.Save();
};
}
public override void DrawTab()
{
using ImRaii.IEndObject endObject = ImRaii.TabItem("Stop###StopConditionns");
if (!endObject)
{
return;
}
bool v = base.Configuration.Stop.Enabled;
if (ImGui.Checkbox("Stop Questionable when any of the conditions below are met", ref v))
{
base.Configuration.Stop.Enabled = v;
Save();
}
ImGui.Separator();
using (ImRaii.Disabled(!v))
{
ImGui.Text("Stop when character level reaches:");
bool v2 = base.Configuration.Stop.LevelToStopAfter;
if (ImGui.Checkbox("Enable level stop condition", ref v2))
{
base.Configuration.Stop.LevelToStopAfter = v2;
Save();
}
using (ImRaii.Disabled(!v2))
{
int data = base.Configuration.Stop.TargetLevel;
ImGui.SetNextItemWidth(100f);
if (ImGui.InputInt("Stop at level", ref data, 1, 5))
{
base.Configuration.Stop.TargetLevel = Math.Max(1, Math.Min(100, data));
Save();
}
int num = _clientState.LocalPlayer?.Level ?? 0;
if (num > 0)
{
ImGui.SameLine();
ImU8String text = new ImU8String(11, 1);
text.AppendLiteral("(Current: ");
text.AppendFormatted(num);
text.AppendLiteral(")");
ImGui.TextDisabled(text);
}
}
ImGui.Separator();
ImGui.Text("Stop when completing any of the quests selected below:");
_questSelector.DrawSelection();
List<ElementId> questsToStopAfter = base.Configuration.Stop.QuestsToStopAfter;
if (questsToStopAfter.Count > 0)
{
using (ImRaii.Disabled(!ImGui.IsKeyDown(ImGuiKey.ModCtrl)))
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Trash, "Clear All"))
{
base.Configuration.Stop.QuestsToStopAfter.Clear();
Save();
}
}
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
{
ImGui.SetTooltip("Hold CTRL to enable this button.");
}
ImGui.Separator();
}
Quest quest = null;
for (int i = 0; i < questsToStopAfter.Count; i++)
{
ElementId elementId = questsToStopAfter[i];
if (!_questRegistry.TryGetQuest(elementId, out Quest quest2))
{
continue;
}
ImU8String text = new ImU8String(5, 1);
text.AppendLiteral("Quest");
text.AppendFormatted(elementId);
using (ImRaii.PushId(text))
{
(Vector4, FontAwesomeIcon, string) questStyle = _uiUtils.GetQuestStyle(elementId);
bool flag;
using (_pluginInterface.UiBuilder.IconFontFixedWidthHandle.Push())
{
ImGui.AlignTextToFramePadding();
ImGui.TextColored(in questStyle.Item1, questStyle.Item2.ToIconString());
flag = ImGui.IsItemHovered();
}
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
ImGui.Text(quest2.Info.Name);
flag |= ImGui.IsItemHovered();
if (flag)
{
_questTooltipComponent.Draw(quest2.Info);
}
using (ImRaii.PushFont(UiBuilder.IconFont))
{
ImGui.SameLine(ImGui.GetContentRegionAvail().X + ImGui.GetStyle().WindowPadding.X - ImGui.CalcTextSize(FontAwesomeIcon.Times.ToIconString()).X - ImGui.GetStyle().FramePadding.X * 2f);
}
if (ImGuiComponents.IconButton($"##Remove{i}", FontAwesomeIcon.Times))
{
quest = quest2;
}
}
}
if (quest != null)
{
base.Configuration.Stop.QuestsToStopAfter.Remove(quest.Id);
Save();
}
}
}
}