forked from liza/Questionable
Restructure config window
This commit is contained in:
parent
097c67ed5d
commit
3820647827
@ -26,6 +26,7 @@ using Questionable.Functions;
|
|||||||
using Questionable.Validation;
|
using Questionable.Validation;
|
||||||
using Questionable.Validation.Validators;
|
using Questionable.Validation.Validators;
|
||||||
using Questionable.Windows;
|
using Questionable.Windows;
|
||||||
|
using Questionable.Windows.ConfigComponents;
|
||||||
using Questionable.Windows.JournalComponents;
|
using Questionable.Windows.JournalComponents;
|
||||||
using Questionable.Windows.QuestComponents;
|
using Questionable.Windows.QuestComponents;
|
||||||
using Action = Questionable.Controller.Steps.Interactions.Action;
|
using Action = Questionable.Controller.Steps.Interactions.Action;
|
||||||
@ -298,6 +299,11 @@ public sealed class QuestionablePlugin : IDalamudPlugin
|
|||||||
serviceCollection.AddSingleton<QuestValidationWindow>();
|
serviceCollection.AddSingleton<QuestValidationWindow>();
|
||||||
serviceCollection.AddSingleton<JournalProgressWindow>();
|
serviceCollection.AddSingleton<JournalProgressWindow>();
|
||||||
serviceCollection.AddSingleton<PriorityWindow>();
|
serviceCollection.AddSingleton<PriorityWindow>();
|
||||||
|
|
||||||
|
serviceCollection.AddSingleton<GeneralConfigComponent>();
|
||||||
|
serviceCollection.AddSingleton<DutyConfigComponent>();
|
||||||
|
serviceCollection.AddSingleton<NotificationConfigComponent>();
|
||||||
|
serviceCollection.AddSingleton<DebugConfigComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddQuestValidators(ServiceCollection serviceCollection)
|
private static void AddQuestValidators(ServiceCollection serviceCollection)
|
||||||
|
64
Questionable/Windows/ConfigComponents/ConfigComponent.cs
Normal file
64
Questionable/Windows/ConfigComponents/ConfigComponent.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace Questionable.Windows.ConfigComponents;
|
||||||
|
|
||||||
|
internal abstract class ConfigComponent
|
||||||
|
{
|
||||||
|
protected const string DutyClipboardSeparator = ";";
|
||||||
|
protected const string DutyWhitelistPrefix = "+";
|
||||||
|
protected const string DutyBlacklistPrefix = "-";
|
||||||
|
|
||||||
|
protected readonly string[] SupportedCfcOptions =
|
||||||
|
[
|
||||||
|
$"{SeIconChar.Circle.ToIconChar()} Enabled (Default)",
|
||||||
|
$"{SeIconChar.Circle.ToIconChar()} Enabled",
|
||||||
|
$"{SeIconChar.Cross.ToIconChar()} Disabled"
|
||||||
|
];
|
||||||
|
|
||||||
|
protected readonly string[] UnsupportedCfcOptions =
|
||||||
|
[
|
||||||
|
$"{SeIconChar.Cross.ToIconChar()} Disabled (Default)",
|
||||||
|
$"{SeIconChar.Circle.ToIconChar()} Enabled",
|
||||||
|
$"{SeIconChar.Cross.ToIconChar()} Disabled"
|
||||||
|
];
|
||||||
|
|
||||||
|
private readonly IDalamudPluginInterface _pluginInterface;
|
||||||
|
|
||||||
|
protected ConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration)
|
||||||
|
{
|
||||||
|
_pluginInterface = pluginInterface;
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Configuration Configuration { get; }
|
||||||
|
|
||||||
|
public abstract void DrawTab();
|
||||||
|
|
||||||
|
protected void Save() => _pluginInterface.SavePluginConfig(Configuration);
|
||||||
|
|
||||||
|
protected static string FormatLevel(int level)
|
||||||
|
{
|
||||||
|
if (level == 0)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return $"{FormatLevel(level / 10)}{(SeIconChar.Number0 + level % 10).ToIconChar()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default implementation for <see cref="ImGui.GetClipboardText"/> throws an NullReferenceException if the clipboard is empty, maybe also if it doesn't contain text.
|
||||||
|
/// </summary>
|
||||||
|
protected unsafe string? GetClipboardText()
|
||||||
|
{
|
||||||
|
byte* ptr = ImGuiNative.igGetClipboardText();
|
||||||
|
if (ptr == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int byteCount = 0;
|
||||||
|
while (ptr[byteCount] != 0)
|
||||||
|
++byteCount;
|
||||||
|
return Encoding.UTF8.GetString(ptr, byteCount);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace Questionable.Windows.ConfigComponents;
|
||||||
|
|
||||||
|
internal sealed class DebugConfigComponent : ConfigComponent
|
||||||
|
{
|
||||||
|
public DebugConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration)
|
||||||
|
: base(pluginInterface, configuration)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawTab()
|
||||||
|
{
|
||||||
|
using var tab = ImRaii.TabItem("Advanced");
|
||||||
|
if (!tab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGui.TextColored(ImGuiColors.DalamudRed,
|
||||||
|
"Enabling any option here may cause unexpected behavior. Use at your own risk.");
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
bool debugOverlay = Configuration.Advanced.DebugOverlay;
|
||||||
|
if (ImGui.Checkbox("Enable debug overlay", ref debugOverlay))
|
||||||
|
{
|
||||||
|
Configuration.Advanced.DebugOverlay = debugOverlay;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool neverFly = Configuration.Advanced.NeverFly;
|
||||||
|
if (ImGui.Checkbox("Disable flying (even if unlocked for the zone)", ref neverFly))
|
||||||
|
{
|
||||||
|
Configuration.Advanced.NeverFly = neverFly;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool additionalStatusInformation = Configuration.Advanced.AdditionalStatusInformation;
|
||||||
|
if (ImGui.Checkbox("Draw additional status information", ref additionalStatusInformation))
|
||||||
|
{
|
||||||
|
Configuration.Advanced.AdditionalStatusInformation = additionalStatusInformation;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
249
Questionable/Windows/ConfigComponents/DutyConfigComponent.cs
Normal file
249
Questionable/Windows/ConfigComponents/DutyConfigComponent.cs
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
|
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 ImGuiNET;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
using Questionable.Controller;
|
||||||
|
using Questionable.Data;
|
||||||
|
using Questionable.External;
|
||||||
|
using Questionable.Model;
|
||||||
|
|
||||||
|
namespace Questionable.Windows.ConfigComponents;
|
||||||
|
|
||||||
|
internal sealed class DutyConfigComponent : ConfigComponent
|
||||||
|
{
|
||||||
|
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 = dataManager.GetExcelSheet<DawnContent>()
|
||||||
|
.Where(x => x is { RowId: > 0, Unknown16: false })
|
||||||
|
.OrderBy(x => x.Unknown15) // SortKey for the support UI
|
||||||
|
.Select(x => x.Content.ValueNullable)
|
||||||
|
.Where(x => x != null)
|
||||||
|
.Select(x => x!.Value)
|
||||||
|
.Select(x => 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,
|
||||||
|
x.SortKey
|
||||||
|
})
|
||||||
|
.GroupBy(x => x.Expansion)
|
||||||
|
.ToDictionary(x => x.Key,
|
||||||
|
x => x
|
||||||
|
.Select(y => new DutyInfo(y.CfcId, y.TerritoryId,
|
||||||
|
$"{SeIconChar.LevelEn.ToIconChar()}{FormatLevel(y.Level)} {y.Name}"))
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawTab()
|
||||||
|
{
|
||||||
|
using var tab = ImRaii.TabItem("Duties");
|
||||||
|
if (!tab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool runInstancedContentWithAutoDuty = Configuration.Duties.RunInstancedContentWithAutoDuty;
|
||||||
|
if (ImGui.Checkbox("Run instanced content with AutoDuty and BossMod", ref runInstancedContentWithAutoDuty))
|
||||||
|
{
|
||||||
|
Configuration.Duties.RunInstancedContentWithAutoDuty = runInstancedContentWithAutoDuty;
|
||||||
|
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(!runInstancedContentWithAutoDuty))
|
||||||
|
{
|
||||||
|
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 dungeon settings for each individual dungeon/trial:");
|
||||||
|
|
||||||
|
DrawConfigTable(runInstancedContentWithAutoDuty);
|
||||||
|
DrawClipboardButtons();
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
using (var unused = ImRaii.Disabled(!ImGui.IsKeyDown(ImGuiKey.ModCtrl)))
|
||||||
|
{
|
||||||
|
if (ImGui.Button("Reset to default"))
|
||||||
|
{
|
||||||
|
Configuration.Duties.WhitelistedDutyCfcIds.Clear();
|
||||||
|
Configuration.Duties.BlacklistedDutyCfcIds.Clear();
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
|
ImGui.SetTooltip("Hold CTRL to enable this button.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawConfigTable(bool runInstancedContentWithAutoDuty)
|
||||||
|
{
|
||||||
|
using var child = ImRaii.Child("DutyConfiguration", new Vector2(-1, 400), true);
|
||||||
|
if (!child)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (EExpansionVersion expansion in Enum.GetValues<EExpansionVersion>())
|
||||||
|
{
|
||||||
|
if (ImGui.CollapsingHeader(expansion.ToString()))
|
||||||
|
{
|
||||||
|
using var table = ImRaii.Table($"Duties{expansion}", 2, ImGuiTableFlags.SizingFixedFit);
|
||||||
|
if (table)
|
||||||
|
{
|
||||||
|
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch);
|
||||||
|
ImGui.TableSetupColumn("Options", ImGuiTableColumnFlags.WidthFixed, 200f);
|
||||||
|
|
||||||
|
if (_contentFinderConditionNames.TryGetValue(expansion, out var cfcNames))
|
||||||
|
{
|
||||||
|
foreach (var (cfcId, territoryId, name) in cfcNames)
|
||||||
|
{
|
||||||
|
if (_questRegistry.TryGetDutyByContentFinderConditionId(cfcId,
|
||||||
|
out bool autoDutyEnabledByDefault))
|
||||||
|
{
|
||||||
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
|
string[] labels = autoDutyEnabledByDefault
|
||||||
|
? SupportedCfcOptions
|
||||||
|
: UnsupportedCfcOptions;
|
||||||
|
int value = 0;
|
||||||
|
if (Configuration.Duties.WhitelistedDutyCfcIds.Contains(cfcId))
|
||||||
|
value = 1;
|
||||||
|
if (Configuration.Duties.BlacklistedDutyCfcIds.Contains(cfcId))
|
||||||
|
value = 2;
|
||||||
|
|
||||||
|
if (ImGui.TableNextColumn())
|
||||||
|
{
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted(name);
|
||||||
|
if (ImGui.IsItemHovered() &&
|
||||||
|
Configuration.Advanced.AdditionalStatusInformation)
|
||||||
|
{
|
||||||
|
using var tooltip = ImRaii.Tooltip();
|
||||||
|
if (tooltip)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(name);
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.BulletText($"TerritoryId: {territoryId}");
|
||||||
|
ImGui.BulletText($"ContentFinderConditionId: {cfcId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runInstancedContentWithAutoDuty && !_autoDutyIpc.HasPath(cfcId))
|
||||||
|
ImGuiComponents.HelpMarker("This duty is not supported by AutoDuty",
|
||||||
|
FontAwesomeIcon.Times, ImGuiColors.DalamudRed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.TableNextColumn())
|
||||||
|
{
|
||||||
|
using var _ = ImRaii.PushId($"##Dungeon{cfcId}");
|
||||||
|
ImGui.SetNextItemWidth(200);
|
||||||
|
if (ImGui.Combo(string.Empty, ref value, labels, labels.Length))
|
||||||
|
{
|
||||||
|
Configuration.Duties.WhitelistedDutyCfcIds.Remove(cfcId);
|
||||||
|
Configuration.Duties.BlacklistedDutyCfcIds.Remove(cfcId);
|
||||||
|
|
||||||
|
if (value == 1)
|
||||||
|
Configuration.Duties.WhitelistedDutyCfcIds.Add(cfcId);
|
||||||
|
else if (value == 2)
|
||||||
|
Configuration.Duties.BlacklistedDutyCfcIds.Add(cfcId);
|
||||||
|
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawClipboardButtons()
|
||||||
|
{
|
||||||
|
using (ImRaii.Disabled(Configuration.Duties.WhitelistedDutyCfcIds.Count +
|
||||||
|
Configuration.Duties.BlacklistedDutyCfcIds.Count == 0))
|
||||||
|
{
|
||||||
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Copy, "Export to clipboard"))
|
||||||
|
{
|
||||||
|
var whitelisted =
|
||||||
|
Configuration.Duties.WhitelistedDutyCfcIds.Select(x => $"{DutyWhitelistPrefix}{x}");
|
||||||
|
var blacklisted =
|
||||||
|
Configuration.Duties.BlacklistedDutyCfcIds.Select(x => $"{DutyBlacklistPrefix}{x}");
|
||||||
|
string text = DutyClipboardPrefix + Convert.ToBase64String(Encoding.UTF8.GetBytes(
|
||||||
|
string.Join(DutyClipboardSeparator, whitelisted.Concat(blacklisted))));
|
||||||
|
ImGui.SetClipboardText(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
string? clipboardText = GetClipboardText();
|
||||||
|
using (ImRaii.Disabled(clipboardText == null ||
|
||||||
|
!clipboardText.StartsWith(DutyClipboardPrefix, StringComparison.InvariantCulture)))
|
||||||
|
{
|
||||||
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Paste, "Import from Clipboard"))
|
||||||
|
{
|
||||||
|
clipboardText = clipboardText!.Substring(DutyClipboardPrefix.Length);
|
||||||
|
string text = Encoding.UTF8.GetString(Convert.FromBase64String(clipboardText));
|
||||||
|
|
||||||
|
Configuration.Duties.WhitelistedDutyCfcIds.Clear();
|
||||||
|
Configuration.Duties.BlacklistedDutyCfcIds.Clear();
|
||||||
|
foreach (string part in text.Split(DutyClipboardSeparator))
|
||||||
|
{
|
||||||
|
if (part.StartsWith(DutyWhitelistPrefix, StringComparison.InvariantCulture) &&
|
||||||
|
uint.TryParse(part.AsSpan(DutyWhitelistPrefix.Length), CultureInfo.InvariantCulture,
|
||||||
|
out uint whitelistedCfcId))
|
||||||
|
Configuration.Duties.WhitelistedDutyCfcIds.Add(whitelistedCfcId);
|
||||||
|
|
||||||
|
if (part.StartsWith(DutyBlacklistPrefix, StringComparison.InvariantCulture) &&
|
||||||
|
uint.TryParse(part.AsSpan(DutyBlacklistPrefix.Length), CultureInfo.InvariantCulture,
|
||||||
|
out uint blacklistedCfcId))
|
||||||
|
Configuration.Duties.WhitelistedDutyCfcIds.Add(blacklistedCfcId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed record DutyInfo(uint CfcId, uint TerritoryId, string Name);
|
||||||
|
}
|
114
Questionable/Windows/ConfigComponents/GeneralConfigComponent.cs
Normal file
114
Questionable/Windows/ConfigComponents/GeneralConfigComponent.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using ImGuiNET;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
using Questionable.Controller;
|
||||||
|
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
||||||
|
|
||||||
|
namespace Questionable.Windows.ConfigComponents;
|
||||||
|
|
||||||
|
internal sealed class GeneralConfigComponent : ConfigComponent
|
||||||
|
{
|
||||||
|
private static readonly List<(uint Id, string Name)> DefaultMounts = [(0, "Mount Roulette")];
|
||||||
|
|
||||||
|
private readonly CombatController _combatController;
|
||||||
|
|
||||||
|
private readonly uint[] _mountIds;
|
||||||
|
private readonly string[] _mountNames;
|
||||||
|
private readonly string[] _combatModuleNames = ["None", "Boss Mod (VBM)", "Wrath Combo", "Rotation Solver Reborn"];
|
||||||
|
|
||||||
|
private readonly string[] _grandCompanyNames =
|
||||||
|
["None (manually pick quest)", "Maelstrom", "Twin Adder", "Immortal Flames"];
|
||||||
|
|
||||||
|
public GeneralConfigComponent(
|
||||||
|
IDalamudPluginInterface pluginInterface,
|
||||||
|
Configuration configuration,
|
||||||
|
CombatController combatController,
|
||||||
|
IDataManager dataManager)
|
||||||
|
: base(pluginInterface, configuration)
|
||||||
|
{
|
||||||
|
_combatController = combatController;
|
||||||
|
|
||||||
|
var mounts = dataManager.GetExcelSheet<Mount>()
|
||||||
|
.Where(x => x is { RowId: > 0, Icon: > 0 })
|
||||||
|
.Select(x => (MountId: x.RowId, Name: x.Singular.ToString()))
|
||||||
|
.Where(x => !string.IsNullOrEmpty(x.Name))
|
||||||
|
.OrderBy(x => x.Name)
|
||||||
|
.ToList();
|
||||||
|
_mountIds = DefaultMounts.Select(x => x.Id).Concat(mounts.Select(x => x.MountId)).ToArray();
|
||||||
|
_mountNames = DefaultMounts.Select(x => x.Name).Concat(mounts.Select(x => x.Name)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawTab()
|
||||||
|
{
|
||||||
|
using var tab = ImRaii.TabItem("General");
|
||||||
|
if (!tab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
using (ImRaii.Disabled(_combatController.IsRunning))
|
||||||
|
{
|
||||||
|
int selectedCombatModule = (int)Configuration.General.CombatModule;
|
||||||
|
if (ImGui.Combo("Preferred Combat Module", ref selectedCombatModule, _combatModuleNames,
|
||||||
|
_combatModuleNames.Length))
|
||||||
|
{
|
||||||
|
Configuration.General.CombatModule = (Configuration.ECombatModule)selectedCombatModule;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int selectedMount = Array.FindIndex(_mountIds, x => x == Configuration.General.MountId);
|
||||||
|
if (selectedMount == -1)
|
||||||
|
{
|
||||||
|
selectedMount = 0;
|
||||||
|
Configuration.General.MountId = _mountIds[selectedMount];
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.Combo("Preferred Mount", ref selectedMount, _mountNames, _mountNames.Length))
|
||||||
|
{
|
||||||
|
Configuration.General.MountId = _mountIds[selectedMount];
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
int grandCompany = (int)Configuration.General.GrandCompany;
|
||||||
|
if (ImGui.Combo("Preferred Grand Company", ref grandCompany, _grandCompanyNames,
|
||||||
|
_grandCompanyNames.Length))
|
||||||
|
{
|
||||||
|
Configuration.General.GrandCompany = (GrandCompany)grandCompany;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hideInAllInstances = Configuration.General.HideInAllInstances;
|
||||||
|
if (ImGui.Checkbox("Hide quest window in all instanced duties", ref hideInAllInstances))
|
||||||
|
{
|
||||||
|
Configuration.General.HideInAllInstances = hideInAllInstances;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useEscToCancelQuesting = Configuration.General.UseEscToCancelQuesting;
|
||||||
|
if (ImGui.Checkbox("Use ESC to cancel questing/movement", ref useEscToCancelQuesting))
|
||||||
|
{
|
||||||
|
Configuration.General.UseEscToCancelQuesting = useEscToCancelQuesting;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool showIncompleteSeasonalEvents = Configuration.General.ShowIncompleteSeasonalEvents;
|
||||||
|
if (ImGui.Checkbox("Show details for incomplete seasonal events", ref showIncompleteSeasonalEvents))
|
||||||
|
{
|
||||||
|
Configuration.General.ShowIncompleteSeasonalEvents = showIncompleteSeasonalEvents;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool configureTextAdvance = Configuration.General.ConfigureTextAdvance;
|
||||||
|
if (ImGui.Checkbox("Automatically configure TextAdvance with the recommended settings",
|
||||||
|
ref configureTextAdvance))
|
||||||
|
{
|
||||||
|
Configuration.General.ConfigureTextAdvance = configureTextAdvance;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Game.Text;
|
||||||
|
using Dalamud.Interface.Components;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
using ImGuiNET;
|
||||||
|
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 var tab = ImRaii.TabItem("Notifications");
|
||||||
|
if (!tab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool enabled = Configuration.Notifications.Enabled;
|
||||||
|
if (ImGui.Checkbox("Enable notifications when manual interaction is required", ref enabled))
|
||||||
|
{
|
||||||
|
Configuration.Notifications.Enabled = enabled;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (ImRaii.Disabled(!Configuration.Notifications.Enabled))
|
||||||
|
{
|
||||||
|
using (ImRaii.PushIndent())
|
||||||
|
{
|
||||||
|
var xivChatTypes = Enum.GetValues<XivChatType>()
|
||||||
|
.Where(x => x != XivChatType.StandardEmote)
|
||||||
|
.ToArray();
|
||||||
|
var selectedChatType = Array.IndexOf(xivChatTypes, Configuration.Notifications.ChatType);
|
||||||
|
string[] chatTypeNames = xivChatTypes
|
||||||
|
.Select(t => t.GetAttribute<XivChatTypeInfoAttribute>()?.FancyName ?? t.ToString())
|
||||||
|
.ToArray();
|
||||||
|
if (ImGui.Combo("Chat channel", ref selectedChatType, chatTypeNames,
|
||||||
|
chatTypeNames.Length))
|
||||||
|
{
|
||||||
|
Configuration.Notifications.ChatType = xivChatTypes[selectedChatType];
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.Text("NotificationMaster settings");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGuiComponents.HelpMarker("Requires the plugin 'NotificationMaster' to be installed.");
|
||||||
|
using (ImRaii.Disabled(!_notificationMasterIpc.Enabled))
|
||||||
|
{
|
||||||
|
bool showTrayMessage = Configuration.Notifications.ShowTrayMessage;
|
||||||
|
if (ImGui.Checkbox("Show tray notification", ref showTrayMessage))
|
||||||
|
{
|
||||||
|
Configuration.Notifications.ShowTrayMessage = showTrayMessage;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool flashTaskbar = Configuration.Notifications.FlashTaskbar;
|
||||||
|
if (ImGui.Checkbox("Flash taskbar icon", ref flashTaskbar))
|
||||||
|
{
|
||||||
|
Configuration.Notifications.FlashTaskbar = flashTaskbar;
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,485 +1,50 @@
|
|||||||
using System;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Text;
|
|
||||||
using Dalamud.Game.Text;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using LLib.ImGui;
|
using LLib.ImGui;
|
||||||
using Lumina.Excel.Sheets;
|
using Questionable.Windows.ConfigComponents;
|
||||||
using Questionable.Controller;
|
|
||||||
using Questionable.Data;
|
|
||||||
using Questionable.External;
|
|
||||||
using Questionable.Model;
|
|
||||||
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
|
||||||
|
|
||||||
namespace Questionable.Windows;
|
namespace Questionable.Windows;
|
||||||
|
|
||||||
internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
|
||||||
{
|
{
|
||||||
private const string DutyClipboardPrefix = "qst:duty:";
|
|
||||||
private const string DutyClipboardSeparator = ";";
|
|
||||||
private const string DutyWhitelistPrefix = "+";
|
|
||||||
private const string DutyBlacklistPrefix = "-";
|
|
||||||
|
|
||||||
private static readonly List<(uint Id, string Name)> DefaultMounts = [(0, "Mount Roulette")];
|
|
||||||
|
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
private readonly IDalamudPluginInterface _pluginInterface;
|
||||||
private readonly NotificationMasterIpc _notificationMasterIpc;
|
private readonly GeneralConfigComponent _generalConfigComponent;
|
||||||
|
private readonly DutyConfigComponent _dutyConfigComponent;
|
||||||
|
private readonly NotificationConfigComponent _notificationConfigComponent;
|
||||||
|
private readonly DebugConfigComponent _debugConfigComponent;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly CombatController _combatController;
|
|
||||||
private readonly QuestRegistry _questRegistry;
|
|
||||||
private readonly AutoDutyIpc _autoDutyIpc;
|
|
||||||
|
|
||||||
private readonly uint[] _mountIds;
|
public ConfigWindow(
|
||||||
private readonly string[] _mountNames;
|
IDalamudPluginInterface pluginInterface,
|
||||||
|
GeneralConfigComponent generalConfigComponent,
|
||||||
private readonly string[] _combatModuleNames = ["None", "Boss Mod (VBM)", "Wrath Combo", "Rotation Solver Reborn"];
|
DutyConfigComponent dutyConfigComponent,
|
||||||
|
NotificationConfigComponent notificationConfigComponent,
|
||||||
private readonly string[] _grandCompanyNames =
|
DebugConfigComponent debugConfigComponent,
|
||||||
["None (manually pick quest)", "Maelstrom", "Twin Adder", "Immortal Flames"];
|
Configuration configuration)
|
||||||
|
|
||||||
private readonly string[] _supportedCfcOptions =
|
|
||||||
[
|
|
||||||
$"{SeIconChar.Circle.ToIconChar()} Enabled (Default)",
|
|
||||||
$"{SeIconChar.Circle.ToIconChar()} Enabled",
|
|
||||||
$"{SeIconChar.Cross.ToIconChar()} Disabled"
|
|
||||||
];
|
|
||||||
|
|
||||||
private readonly string[] _unsupportedCfcOptions =
|
|
||||||
[
|
|
||||||
$"{SeIconChar.Cross.ToIconChar()} Disabled (Default)",
|
|
||||||
$"{SeIconChar.Circle.ToIconChar()} Enabled",
|
|
||||||
$"{SeIconChar.Cross.ToIconChar()} Disabled"
|
|
||||||
];
|
|
||||||
|
|
||||||
private readonly Dictionary<EExpansionVersion, List<DutyInfo>> _contentFinderConditionNames;
|
|
||||||
|
|
||||||
public ConfigWindow(IDalamudPluginInterface pluginInterface,
|
|
||||||
NotificationMasterIpc notificationMasterIpc,
|
|
||||||
Configuration configuration,
|
|
||||||
IDataManager dataManager,
|
|
||||||
CombatController combatController,
|
|
||||||
TerritoryData territoryData,
|
|
||||||
QuestRegistry questRegistry,
|
|
||||||
AutoDutyIpc autoDutyIpc)
|
|
||||||
: base("Config - Questionable###QuestionableConfig", ImGuiWindowFlags.AlwaysAutoResize)
|
: base("Config - Questionable###QuestionableConfig", ImGuiWindowFlags.AlwaysAutoResize)
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_notificationMasterIpc = notificationMasterIpc;
|
_generalConfigComponent = generalConfigComponent;
|
||||||
|
_dutyConfigComponent = dutyConfigComponent;
|
||||||
|
_notificationConfigComponent = notificationConfigComponent;
|
||||||
|
_debugConfigComponent = debugConfigComponent;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_combatController = combatController;
|
|
||||||
_questRegistry = questRegistry;
|
|
||||||
_autoDutyIpc = autoDutyIpc;
|
|
||||||
|
|
||||||
var mounts = dataManager.GetExcelSheet<Mount>()
|
|
||||||
.Where(x => x is { RowId: > 0, Icon: > 0 })
|
|
||||||
.Select(x => (MountId: x.RowId, Name: x.Singular.ToString()))
|
|
||||||
.Where(x => !string.IsNullOrEmpty(x.Name))
|
|
||||||
.OrderBy(x => x.Name)
|
|
||||||
.ToList();
|
|
||||||
_mountIds = DefaultMounts.Select(x => x.Id).Concat(mounts.Select(x => x.MountId)).ToArray();
|
|
||||||
_mountNames = DefaultMounts.Select(x => x.Name).Concat(mounts.Select(x => x.Name)).ToArray();
|
|
||||||
|
|
||||||
_contentFinderConditionNames = dataManager.GetExcelSheet<DawnContent>()
|
|
||||||
.Where(x => x is { RowId: > 0, Unknown16: false })
|
|
||||||
.OrderBy(x => x.Unknown15) // SortKey for the support UI
|
|
||||||
.Select(x => x.Content.ValueNullable)
|
|
||||||
.Where(x => x != null)
|
|
||||||
.Select(x => x!.Value)
|
|
||||||
.Select(x => 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,
|
|
||||||
x.SortKey
|
|
||||||
})
|
|
||||||
.GroupBy(x => x.Expansion)
|
|
||||||
.ToDictionary(x => x.Key,
|
|
||||||
x => x
|
|
||||||
.Select(y => new DutyInfo(y.CfcId, y.TerritoryId, $"{SeIconChar.LevelEn.ToIconChar()}{FormatLevel(y.Level)} {y.Name}"))
|
|
||||||
.ToList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WindowConfig WindowConfig => _configuration.ConfigWindowConfig;
|
public WindowConfig WindowConfig => _configuration.ConfigWindowConfig;
|
||||||
|
|
||||||
private static string FormatLevel(int level)
|
|
||||||
{
|
|
||||||
if (level == 0)
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
return $"{FormatLevel(level / 10)}{(SeIconChar.Number0 + level % 10).ToIconChar()}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
using var tabBar = ImRaii.TabBar("QuestionableConfigTabs");
|
using var tabBar = ImRaii.TabBar("QuestionableConfigTabs");
|
||||||
if (!tabBar)
|
if (!tabBar)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DrawGeneralTab();
|
_generalConfigComponent.DrawTab();
|
||||||
DrawDutiesTab();
|
_dutyConfigComponent.DrawTab();
|
||||||
DrawNotificationsTab();
|
_notificationConfigComponent.DrawTab();
|
||||||
DrawAdvancedTab();
|
_debugConfigComponent.DrawTab();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGeneralTab()
|
public void SaveWindowConfig() => _pluginInterface.SavePluginConfig(_configuration);
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("General");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.Disabled(_combatController.IsRunning))
|
|
||||||
{
|
|
||||||
int selectedCombatModule = (int)_configuration.General.CombatModule;
|
|
||||||
if (ImGui.Combo("Preferred Combat Module", ref selectedCombatModule, _combatModuleNames,
|
|
||||||
_combatModuleNames.Length))
|
|
||||||
{
|
|
||||||
_configuration.General.CombatModule = (Configuration.ECombatModule)selectedCombatModule;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int selectedMount = Array.FindIndex(_mountIds, x => x == _configuration.General.MountId);
|
|
||||||
if (selectedMount == -1)
|
|
||||||
{
|
|
||||||
selectedMount = 0;
|
|
||||||
_configuration.General.MountId = _mountIds[selectedMount];
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.Combo("Preferred Mount", ref selectedMount, _mountNames, _mountNames.Length))
|
|
||||||
{
|
|
||||||
_configuration.General.MountId = _mountIds[selectedMount];
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
int grandCompany = (int)_configuration.General.GrandCompany;
|
|
||||||
if (ImGui.Combo("Preferred Grand Company", ref grandCompany, _grandCompanyNames,
|
|
||||||
_grandCompanyNames.Length))
|
|
||||||
{
|
|
||||||
_configuration.General.GrandCompany = (GrandCompany)grandCompany;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hideInAllInstances = _configuration.General.HideInAllInstances;
|
|
||||||
if (ImGui.Checkbox("Hide quest window in all instanced duties", ref hideInAllInstances))
|
|
||||||
{
|
|
||||||
_configuration.General.HideInAllInstances = hideInAllInstances;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool useEscToCancelQuesting = _configuration.General.UseEscToCancelQuesting;
|
|
||||||
if (ImGui.Checkbox("Use ESC to cancel questing/movement", ref useEscToCancelQuesting))
|
|
||||||
{
|
|
||||||
_configuration.General.UseEscToCancelQuesting = useEscToCancelQuesting;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool showIncompleteSeasonalEvents = _configuration.General.ShowIncompleteSeasonalEvents;
|
|
||||||
if (ImGui.Checkbox("Show details for incomplete seasonal events", ref showIncompleteSeasonalEvents))
|
|
||||||
{
|
|
||||||
_configuration.General.ShowIncompleteSeasonalEvents = showIncompleteSeasonalEvents;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool configureTextAdvance = _configuration.General.ConfigureTextAdvance;
|
|
||||||
if (ImGui.Checkbox("Automatically configure TextAdvance with the recommended settings",
|
|
||||||
ref configureTextAdvance))
|
|
||||||
{
|
|
||||||
_configuration.General.ConfigureTextAdvance = configureTextAdvance;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawDutiesTab()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Duties");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool runInstancedContentWithAutoDuty = _configuration.Duties.RunInstancedContentWithAutoDuty;
|
|
||||||
if (ImGui.Checkbox("Run instanced content with AutoDuty and BossMod", ref runInstancedContentWithAutoDuty))
|
|
||||||
{
|
|
||||||
_configuration.Duties.RunInstancedContentWithAutoDuty = runInstancedContentWithAutoDuty;
|
|
||||||
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(!runInstancedContentWithAutoDuty))
|
|
||||||
{
|
|
||||||
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 dungeon settings for each individual dungeon/trial:");
|
|
||||||
|
|
||||||
using (var child = ImRaii.Child("DutyConfiguration", new Vector2(-1, 400), true))
|
|
||||||
{
|
|
||||||
if (child)
|
|
||||||
{
|
|
||||||
foreach (EExpansionVersion expansion in Enum.GetValues<EExpansionVersion>())
|
|
||||||
{
|
|
||||||
if (ImGui.CollapsingHeader(expansion.ToString()))
|
|
||||||
{
|
|
||||||
using var table = ImRaii.Table($"Duties{expansion}", 2, ImGuiTableFlags.SizingFixedFit);
|
|
||||||
if (table)
|
|
||||||
{
|
|
||||||
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch);
|
|
||||||
ImGui.TableSetupColumn("Options", ImGuiTableColumnFlags.WidthFixed, 200f);
|
|
||||||
|
|
||||||
if (_contentFinderConditionNames.TryGetValue(expansion, out var cfcNames))
|
|
||||||
{
|
|
||||||
foreach (var (cfcId, territoryId, name) in cfcNames)
|
|
||||||
{
|
|
||||||
if (_questRegistry.TryGetDutyByContentFinderConditionId(cfcId,
|
|
||||||
out bool autoDutyEnabledByDefault))
|
|
||||||
{
|
|
||||||
ImGui.TableNextRow();
|
|
||||||
|
|
||||||
string[] labels = autoDutyEnabledByDefault
|
|
||||||
? _supportedCfcOptions
|
|
||||||
: _unsupportedCfcOptions;
|
|
||||||
int value = 0;
|
|
||||||
if (_configuration.Duties.WhitelistedDutyCfcIds.Contains(cfcId))
|
|
||||||
value = 1;
|
|
||||||
if (_configuration.Duties.BlacklistedDutyCfcIds.Contains(cfcId))
|
|
||||||
value = 2;
|
|
||||||
|
|
||||||
if (ImGui.TableNextColumn())
|
|
||||||
{
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.TextUnformatted(name);
|
|
||||||
if (ImGui.IsItemHovered() && _configuration.Advanced.AdditionalStatusInformation)
|
|
||||||
{
|
|
||||||
using var tooltip = ImRaii.Tooltip();
|
|
||||||
if (tooltip)
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted(name);
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.BulletText($"TerritoryId: {territoryId}");
|
|
||||||
ImGui.BulletText($"ContentFinderConditionId: {cfcId}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runInstancedContentWithAutoDuty && !_autoDutyIpc.HasPath(cfcId))
|
|
||||||
ImGuiComponents.HelpMarker("This duty is not supported by AutoDuty", FontAwesomeIcon.Times, ImGuiColors.DalamudRed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.TableNextColumn())
|
|
||||||
{
|
|
||||||
using var _ = ImRaii.PushId($"##Dungeon{cfcId}");
|
|
||||||
ImGui.SetNextItemWidth(200);
|
|
||||||
if (ImGui.Combo(string.Empty, ref value, labels, labels.Length))
|
|
||||||
{
|
|
||||||
_configuration.Duties.WhitelistedDutyCfcIds.Remove(cfcId);
|
|
||||||
_configuration.Duties.BlacklistedDutyCfcIds.Remove(cfcId);
|
|
||||||
|
|
||||||
if (value == 1)
|
|
||||||
_configuration.Duties.WhitelistedDutyCfcIds.Add(cfcId);
|
|
||||||
else if (value == 2)
|
|
||||||
_configuration.Duties.BlacklistedDutyCfcIds.Add(cfcId);
|
|
||||||
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using (ImRaii.Disabled(_configuration.Duties.WhitelistedDutyCfcIds.Count +
|
|
||||||
_configuration.Duties.BlacklistedDutyCfcIds.Count == 0))
|
|
||||||
{
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Copy, "Export to clipboard"))
|
|
||||||
{
|
|
||||||
var whitelisted =
|
|
||||||
_configuration.Duties.WhitelistedDutyCfcIds.Select(x => $"{DutyWhitelistPrefix}{x}");
|
|
||||||
var blacklisted =
|
|
||||||
_configuration.Duties.BlacklistedDutyCfcIds.Select(x => $"{DutyBlacklistPrefix}{x}");
|
|
||||||
string text = DutyClipboardPrefix + Convert.ToBase64String(Encoding.UTF8.GetBytes(
|
|
||||||
string.Join(DutyClipboardSeparator, whitelisted.Concat(blacklisted))));
|
|
||||||
ImGui.SetClipboardText(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
string? clipboardText = GetClipboardText();
|
|
||||||
using (ImRaii.Disabled(clipboardText == null || !clipboardText.StartsWith(DutyClipboardPrefix, StringComparison.InvariantCulture)))
|
|
||||||
{
|
|
||||||
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Paste, "Import from Clipboard"))
|
|
||||||
{
|
|
||||||
clipboardText = clipboardText!.Substring(DutyClipboardPrefix.Length);
|
|
||||||
string text = Encoding.UTF8.GetString(Convert.FromBase64String(clipboardText));
|
|
||||||
|
|
||||||
_configuration.Duties.WhitelistedDutyCfcIds.Clear();
|
|
||||||
_configuration.Duties.BlacklistedDutyCfcIds.Clear();
|
|
||||||
foreach (string part in text.Split(DutyClipboardSeparator))
|
|
||||||
{
|
|
||||||
if (part.StartsWith(DutyWhitelistPrefix, StringComparison.InvariantCulture) &&
|
|
||||||
uint.TryParse(part.AsSpan(DutyWhitelistPrefix.Length), CultureInfo.InvariantCulture,
|
|
||||||
out uint whitelistedCfcId))
|
|
||||||
_configuration.Duties.WhitelistedDutyCfcIds.Add(whitelistedCfcId);
|
|
||||||
|
|
||||||
if (part.StartsWith(DutyBlacklistPrefix, StringComparison.InvariantCulture) &&
|
|
||||||
uint.TryParse(part.AsSpan(DutyBlacklistPrefix.Length), CultureInfo.InvariantCulture,
|
|
||||||
out uint blacklistedCfcId))
|
|
||||||
_configuration.Duties.WhitelistedDutyCfcIds.Add(blacklistedCfcId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
using (var unused = ImRaii.Disabled(!ImGui.IsKeyDown(ImGuiKey.ModCtrl)))
|
|
||||||
{
|
|
||||||
if (ImGui.Button("Reset to default"))
|
|
||||||
{
|
|
||||||
_configuration.Duties.WhitelistedDutyCfcIds.Clear();
|
|
||||||
_configuration.Duties.BlacklistedDutyCfcIds.Clear();
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
|
||||||
ImGui.SetTooltip("Hold CTRL to enable this button.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawNotificationsTab()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Notifications");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool enabled = _configuration.Notifications.Enabled;
|
|
||||||
if (ImGui.Checkbox("Enable notifications when manual interaction is required", ref enabled))
|
|
||||||
{
|
|
||||||
_configuration.Notifications.Enabled = enabled;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
using (ImRaii.Disabled(!_configuration.Notifications.Enabled))
|
|
||||||
{
|
|
||||||
using (ImRaii.PushIndent())
|
|
||||||
{
|
|
||||||
var xivChatTypes = Enum.GetValues<XivChatType>()
|
|
||||||
.Where(x => x != XivChatType.StandardEmote)
|
|
||||||
.ToArray();
|
|
||||||
var selectedChatType = Array.IndexOf(xivChatTypes, _configuration.Notifications.ChatType);
|
|
||||||
string[] chatTypeNames = xivChatTypes
|
|
||||||
.Select(t => t.GetAttribute<XivChatTypeInfoAttribute>()?.FancyName ?? t.ToString())
|
|
||||||
.ToArray();
|
|
||||||
if (ImGui.Combo("Chat channel", ref selectedChatType, chatTypeNames,
|
|
||||||
chatTypeNames.Length))
|
|
||||||
{
|
|
||||||
_configuration.Notifications.ChatType = xivChatTypes[selectedChatType];
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Text("NotificationMaster settings");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGuiComponents.HelpMarker("Requires the plugin 'NotificationMaster' to be installed.");
|
|
||||||
using (ImRaii.Disabled(!_notificationMasterIpc.Enabled))
|
|
||||||
{
|
|
||||||
bool showTrayMessage = _configuration.Notifications.ShowTrayMessage;
|
|
||||||
if (ImGui.Checkbox("Show tray notification", ref showTrayMessage))
|
|
||||||
{
|
|
||||||
_configuration.Notifications.ShowTrayMessage = showTrayMessage;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool flashTaskbar = _configuration.Notifications.FlashTaskbar;
|
|
||||||
if (ImGui.Checkbox("Flash taskbar icon", ref flashTaskbar))
|
|
||||||
{
|
|
||||||
_configuration.Notifications.FlashTaskbar = flashTaskbar;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawAdvancedTab()
|
|
||||||
{
|
|
||||||
using var tab = ImRaii.TabItem("Advanced");
|
|
||||||
if (!tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed,
|
|
||||||
"Enabling any option here may cause unexpected behavior. Use at your own risk.");
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
bool debugOverlay = _configuration.Advanced.DebugOverlay;
|
|
||||||
if (ImGui.Checkbox("Enable debug overlay", ref debugOverlay))
|
|
||||||
{
|
|
||||||
_configuration.Advanced.DebugOverlay = debugOverlay;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool neverFly = _configuration.Advanced.NeverFly;
|
|
||||||
if (ImGui.Checkbox("Disable flying (even if unlocked for the zone)", ref neverFly))
|
|
||||||
{
|
|
||||||
_configuration.Advanced.NeverFly = neverFly;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool additionalStatusInformation = _configuration.Advanced.AdditionalStatusInformation;
|
|
||||||
if (ImGui.Checkbox("Draw additional status information", ref additionalStatusInformation))
|
|
||||||
{
|
|
||||||
_configuration.Advanced.AdditionalStatusInformation = additionalStatusInformation;
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Save() => _pluginInterface.SavePluginConfig(_configuration);
|
|
||||||
|
|
||||||
public void SaveWindowConfig() => Save();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The default implementation for <see cref="ImGui.GetClipboardText"/> throws an NullReferenceException if the clipboard is empty, maybe also if it doesn't contain text.
|
|
||||||
/// </summary>
|
|
||||||
private unsafe string? GetClipboardText()
|
|
||||||
{
|
|
||||||
byte* ptr = ImGuiNative.igGetClipboardText();
|
|
||||||
if (ptr == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
int byteCount = 0;
|
|
||||||
while (ptr[byteCount] != 0)
|
|
||||||
++byteCount;
|
|
||||||
return Encoding.UTF8.GetString(ptr, byteCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private sealed record DutyInfo(uint CfcId, uint TerritoryId, string Name);
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user