-Subproject commit 6dfc18ee6a187138036ee2d51ba2257741c1e568
+Subproject commit 912a7b04ce180e337af9beeef2d1393b040c1ba8
internal sealed class Configuration : IPluginConfiguration
{
+ public const int PluginSetupVersion = 1;
+
public int Version { get; set; } = 1;
+ public int PluginSetupCompleteVersion { get; set; }
public GeneralConfiguration General { get; } = new();
public AdvancedConfiguration Advanced { get; } = new();
public WindowConfig DebugWindowConfig { get; } = new();
public WindowConfig ConfigWindowConfig { get; } = new();
+ internal bool IsPluginSetupComplete() => PluginSetupCompleteVersion == PluginSetupVersion;
+
+ internal void MarkPluginSetupComplete() => PluginSetupCompleteVersion = PluginSetupVersion;
+
internal sealed class GeneralConfiguration
{
public uint MountId { get; set; } = 71;
private readonly QuestRegistry _questRegistry;
private readonly ConfigWindow _configWindow;
private readonly DebugOverlay _debugOverlay;
+ private readonly OneTimeSetupWindow _oneTimeSetupWindow;
private readonly QuestWindow _questWindow;
private readonly QuestSelectionWindow _questSelectionWindow;
private readonly ITargetManager _targetManager;
private readonly QuestFunctions _questFunctions;
private readonly GameFunctions _gameFunctions;
private readonly IDataManager _dataManager;
+ private readonly Configuration _configuration;
public CommandHandler(
ICommandManager commandManager,
QuestRegistry questRegistry,
ConfigWindow configWindow,
DebugOverlay debugOverlay,
+ OneTimeSetupWindow oneTimeSetupWindow,
QuestWindow questWindow,
QuestSelectionWindow questSelectionWindow,
ITargetManager targetManager,
QuestFunctions questFunctions,
GameFunctions gameFunctions,
- IDataManager dataManager)
+ IDataManager dataManager,
+ Configuration configuration)
{
_commandManager = commandManager;
_chatGui = chatGui;
_questRegistry = questRegistry;
_configWindow = configWindow;
_debugOverlay = debugOverlay;
+ _oneTimeSetupWindow = oneTimeSetupWindow;
_questWindow = questWindow;
_questSelectionWindow = questSelectionWindow;
_targetManager = targetManager;
_questFunctions = questFunctions;
_gameFunctions = gameFunctions;
_dataManager = dataManager;
+ _configuration = configuration;
_commandManager.AddHandler("/qst", new CommandInfo(ProcessCommand)
{
private void ProcessCommand(string command, string arguments)
{
+ if (!_configuration.IsPluginSetupComplete())
+ {
+ if (string.IsNullOrEmpty(arguments))
+ _oneTimeSetupWindow.IsOpen = true;
+ else
+ _chatGui.PrintError("Please complete the one-time setup first.", MessageTag, TagColor);
+ return;
+ }
+
string[] parts = arguments.Split(' ');
switch (parts[0])
{
private readonly QuestController _questController;
private readonly MovementController _movementController;
private readonly WindowSystem _windowSystem;
+ private readonly OneTimeSetupWindow _oneTimeSetupWindow;
private readonly QuestWindow _questWindow;
private readonly ConfigWindow _configWindow;
private readonly IToastGui _toastGui;
+ private readonly Configuration _configuration;
private readonly ILogger<DalamudInitializer> _logger;
public DalamudInitializer(
MovementController movementController,
InteractionUiController interactionUiController,
WindowSystem windowSystem,
+ OneTimeSetupWindow oneTimeSetupWindow,
QuestWindow questWindow,
DebugOverlay debugOverlay,
ConfigWindow configWindow,
JournalProgressWindow journalProgressWindow,
PriorityWindow priorityWindow,
IToastGui toastGui,
+ Configuration configuration,
ILogger<DalamudInitializer> logger)
{
_pluginInterface = pluginInterface;
_questController = questController;
_movementController = movementController;
_windowSystem = windowSystem;
+ _oneTimeSetupWindow = oneTimeSetupWindow;
_questWindow = questWindow;
_configWindow = configWindow;
_toastGui = toastGui;
+ _configuration = configuration;
_logger = logger;
+ _windowSystem.AddWindow(oneTimeSetupWindow);
_windowSystem.AddWindow(questWindow);
_windowSystem.AddWindow(configWindow);
_windowSystem.AddWindow(debugOverlay);
_windowSystem.AddWindow(priorityWindow);
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
- _pluginInterface.UiBuilder.OpenMainUi += _questWindow.Toggle;
+ _pluginInterface.UiBuilder.OpenMainUi += ToggleQuestWindow;
_pluginInterface.UiBuilder.OpenConfigUi += _configWindow.Toggle;
_framework.Update += FrameworkUpdate;
_framework.RunOnTick(interactionUiController.HandleCurrentDialogueChoices, TimeSpan.FromMilliseconds(200));
private void OnQuestToast(ref SeString message, ref QuestToastOptions options, ref bool isHandled)
=> _logger.LogTrace("Quest Toast: {Message}", message);
+ private void ToggleQuestWindow()
+ {
+ if (_configuration.IsPluginSetupComplete())
+ _questWindow.Toggle();
+ else
+ _oneTimeSetupWindow.IsOpen = true;
+ }
+
public void Dispose()
{
_toastGui.QuestToast -= OnQuestToast;
_toastGui.Toast -= OnToast;
_framework.Update -= FrameworkUpdate;
_pluginInterface.UiBuilder.OpenConfigUi -= _configWindow.Toggle;
- _pluginInterface.UiBuilder.OpenMainUi -= _questWindow.Toggle;
+ _pluginInterface.UiBuilder.OpenMainUi -= ToggleQuestWindow;
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
_windowSystem.RemoveAllWindows();
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
+using LLib;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Questionable.Controller;
serviceCollection.AddSingleton<GameFunctions>();
serviceCollection.AddSingleton<ChatFunctions>();
serviceCollection.AddSingleton<QuestFunctions>();
+ serviceCollection.AddSingleton<DalamudReflector>();
serviceCollection.AddSingleton<AutoSnipeHandler>();
serviceCollection.AddSingleton<AetherCurrentData>();
serviceCollection.AddSingleton<QuestJournalComponent>();
serviceCollection.AddSingleton<GatheringJournalComponent>();
+ serviceCollection.AddSingleton<OneTimeSetupWindow>();
serviceCollection.AddSingleton<QuestWindow>();
serviceCollection.AddSingleton<ConfigWindow>();
serviceCollection.AddSingleton<DebugOverlay>();
--- /dev/null
+using System;
+using System.Collections.Generic;
+using Dalamud.Interface;
+using Dalamud.Interface.Colors;
+using Dalamud.Interface.Components;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Plugin;
+using Dalamud.Utility;
+using ImGuiNET;
+using LLib;
+using LLib.ImGui;
+using Microsoft.Extensions.Logging;
+
+namespace Questionable.Windows;
+
+internal sealed class OneTimeSetupWindow : LWindow, IDisposable
+{
+ private static readonly IReadOnlyList<PluginInfo> RequiredPlugins =
+ [
+ new("vnavmesh",
+ """
+ vnavmesh handles the navigation within a zone, moving
+ your character to the next quest-related objective.
+ """,
+ new Uri("https://github.com/awgil/ffxiv_navmesh/")),
+ new("Lifestream",
+ """
+ Used to travel to aethernet shards in cities.
+ """,
+ new Uri("https://github.com/NightmareXIV/Lifestream")),
+ new("TextAdvance",
+ """
+ Automatically accepts and turns in quests, skips cutscenes
+ and dialogue.
+ """,
+ new Uri("https://github.com/NightmareXIV/TextAdvance")),
+ ];
+
+ private static readonly IReadOnlyList<PluginInfo> RecommendedPlugins =
+ [
+ new("Rotation Solver Reborn",
+ """
+ Automatically handles most combat interactions you encounter
+ during quests, including being interrupted by mobs.
+ """,
+ new Uri("https://github.com/FFXIV-CombatReborn/RotationSolverReborn")),
+ ];
+
+ private readonly Configuration _configuration;
+ private readonly IDalamudPluginInterface _pluginInterface;
+ private readonly UiUtils _uiUtils;
+ private readonly DalamudReflector _dalamudReflector;
+ private readonly ILogger<OneTimeSetupWindow> _logger;
+
+ public OneTimeSetupWindow(Configuration configuration, IDalamudPluginInterface pluginInterface, UiUtils uiUtils,
+ DalamudReflector dalamudReflector, ILogger<OneTimeSetupWindow> logger)
+ : base("Questionable Setup###QuestionableOneTimeSetup",
+ ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings, true)
+ {
+ _configuration = configuration;
+ _pluginInterface = pluginInterface;
+ _uiUtils = uiUtils;
+ _dalamudReflector = dalamudReflector;
+ _logger = logger;
+
+ RespectCloseHotkey = false;
+ ShowCloseButton = false;
+ AllowPinning = false;
+ AllowClickthrough = false;
+ IsOpen = !_configuration.IsPluginSetupComplete();
+ _logger.LogInformation("One-time setup needed: {IsOpen}", IsOpen);
+ }
+
+ public override void Draw()
+ {
+ 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:");
+ bool allRequiredInstalled = true;
+ using (ImRaii.PushIndent())
+ {
+ foreach (var plugin in RequiredPlugins)
+ allRequiredInstalled &= DrawPlugin(plugin, checklistPadding);
+ }
+
+ ImGui.Spacing();
+ ImGui.Separator();
+ ImGui.Spacing();
+
+ ImGui.Text("The following plugins are recommended, but not required:");
+ using (ImRaii.PushIndent())
+ {
+ foreach (var plugin in RecommendedPlugins)
+ DrawPlugin(plugin, checklistPadding);
+ }
+
+ ImGui.Spacing();
+ ImGui.Separator();
+ ImGui.Spacing();
+
+ if (allRequiredInstalled)
+ {
+ using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.ParsedGreen))
+ {
+ if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Finish Setup"))
+ {
+ _configuration.MarkPluginSetupComplete();
+ _pluginInterface.SavePluginConfig(_configuration);
+ }
+ }
+ }
+ else
+ {
+ using (ImRaii.Disabled())
+ {
+ using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed))
+ ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Check, "Missing required plugins");
+ }
+ }
+
+ ImGui.SameLine();
+
+ if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, "Close window & don't enable Questionable"))
+ {
+ IsOpen = false;
+ }
+ }
+
+ private bool DrawPlugin(PluginInfo plugin, float checklistPadding)
+ {
+ bool isInstalled = IsPluginInstalled(plugin.DisplayName);
+ using (ImRaii.PushId("plugin_" + plugin.DisplayName))
+ {
+ _uiUtils.ChecklistItem(plugin.DisplayName, isInstalled);
+ using (ImRaii.PushIndent(checklistPadding))
+ {
+ ImGui.TextUnformatted(plugin.Details);
+ if (!isInstalled && ImGui.Button("Open Repository"))
+ Util.OpenLink(plugin.Uri.ToString());
+ }
+ }
+
+ return isInstalled;
+ }
+
+ private bool IsPluginInstalled(string internalName)
+ {
+ return _dalamudReflector.TryGetDalamudPlugin(internalName, out _, suppressErrors: true, ignoreCache: true);
+ }
+
+ public void Dispose()
+ {
+ }
+
+ private sealed record PluginInfo(
+ string DisplayName,
+ string Details,
+ Uri Uri);
+}
public override bool DrawConditions()
{
+ if (!_configuration.IsPluginSetupComplete())
+ return false;
+
if (!_clientState.IsLoggedIn || _clientState.LocalPlayer == null || _clientState.IsPvPExcludingDen)
return false;