Remove 'Automatically complete snipe tasks'
authorLiza Carvelli <liza@carvel.li>
Fri, 22 Nov 2024 20:52:35 +0000 (21:52 +0100)
committerLiza Carvelli <liza@carvel.li>
Fri, 22 Nov 2024 20:52:35 +0000 (21:52 +0100)
Questionable/Configuration.cs
Questionable/Controller/Steps/Common/SendNotification.cs
Questionable/Controller/Steps/Interactions/Interact.cs
Questionable/Controller/Utils/AutoSnipeHandler.cs [deleted file]
Questionable/External/AutomatonIpc.cs [new file with mode: 0644]
Questionable/QuestionablePlugin.cs
Questionable/Windows/ConfigWindow.cs
Questionable/Windows/OneTimeSetupWindow.cs

index 238e4a511adf1496722f97df121e2240dd265d26..29358b34eaf241c0e12897859aa13257206eff9a 100644 (file)
@@ -7,7 +7,7 @@ namespace Questionable;
 
 internal sealed class Configuration : IPluginConfiguration
 {
-    public const int PluginSetupVersion = 1;
+    public const int PluginSetupVersion = 2;
 
     public int Version { get; set; } =1 ;
     public int PluginSetupCompleteVersion { get; set; }
@@ -28,7 +28,6 @@ internal sealed class Configuration : IPluginConfiguration
         public bool HideInAllInstances { get; set; } = true;
         public bool UseEscToCancelQuesting { get; set; } = true;
         public bool ShowIncompleteSeasonalEvents { get; set; } = true;
-        public bool AutomaticallyCompleteSnipeTasks { get; set; }
         public bool ConfigureTextAdvance { get; set; } = true;
     }
 
index 5edb743cd42631772e739bd0ab1fd25ed50bd6ef..7334ca075ac059f7b66851f32deda8c89b1c7c58 100644 (file)
@@ -12,14 +12,14 @@ namespace Questionable.Controller.Steps.Common;
 internal static class SendNotification
 {
     internal sealed class Factory(
-        Configuration configuration,
+        AutomatonIpc automatonIpc,
         TerritoryData territoryData) : SimpleTaskFactory
     {
         public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
         {
             return step.InteractionType switch
             {
-                EInteractionType.Snipe when !configuration.General.AutomaticallyCompleteSnipeTasks =>
+                EInteractionType.Snipe when !automatonIpc.IsAutoSnipeEnabled =>
                     new Task(step.InteractionType, step.Comment),
                 EInteractionType.Duty =>
                     new Task(step.InteractionType, step.ContentFinderConditionId.HasValue
index ac8f5109a9d9936e63587cfb4c73defdd7954e51..0ca70c11f0f01aa5e7454c3565b43abaadff22ce 100644 (file)
@@ -8,6 +8,7 @@ using FFXIVClientStructs.FFXIV.Client.Game;
 using Microsoft.Extensions.Logging;
 using Questionable.Controller.Steps.Shared;
 using Questionable.Controller.Utils;
+using Questionable.External;
 using Questionable.Functions;
 using Questionable.Model;
 using Questionable.Model.Questing;
@@ -16,7 +17,7 @@ namespace Questionable.Controller.Steps.Interactions;
 
 internal static class Interact
 {
-    internal sealed class Factory(Configuration configuration) : ITaskFactory
+    internal sealed class Factory(AutomatonIpc automatonIpc) : ITaskFactory
     {
         public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
         {
@@ -43,7 +44,7 @@ internal static class Interact
             }
             else if (step.InteractionType == EInteractionType.Snipe)
             {
-                if (!configuration.General.AutomaticallyCompleteSnipeTasks)
+                if (!automatonIpc.IsAutoSnipeEnabled)
                     yield break;
             }
             else if (step.InteractionType != EInteractionType.Interact)
diff --git a/Questionable/Controller/Utils/AutoSnipeHandler.cs b/Questionable/Controller/Utils/AutoSnipeHandler.cs
deleted file mode 100644 (file)
index dc48c3a..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using Dalamud.Hooking;
-using Dalamud.Plugin.Services;
-using FFXIVClientStructs.FFXIV.Client.Game.Event;
-using FFXIVClientStructs.FFXIV.Common.Lua;
-
-namespace Questionable.Controller.Utils;
-
-internal sealed unsafe class AutoSnipeHandler : IDisposable
-{
-    private readonly QuestController _questController;
-    private readonly Configuration _configuration;
-    private readonly Hook<EnqueueSnipeTaskDelegate> _enqueueSnipeTaskHook;
-
-    private delegate ulong EnqueueSnipeTaskDelegate(EventSceneModuleImplBase* scene, lua_State* state);
-
-    public AutoSnipeHandler(QuestController questController, Configuration configuration, IGameInteropProvider gameInteropProvider)
-    {
-        _questController = questController;
-        _configuration = configuration;
-
-        _enqueueSnipeTaskHook =
-            gameInteropProvider.HookFromSignature<EnqueueSnipeTaskDelegate>(
-                "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 48 8B F1 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8B 4C 24 ??",
-                EnqueueSnipeTask);
-    }
-
-    public void Enable() => _enqueueSnipeTaskHook.Enable();
-
-    private ulong EnqueueSnipeTask(EventSceneModuleImplBase* scene, lua_State* state)
-    {
-        if (_configuration.General.AutomaticallyCompleteSnipeTasks && _questController.IsRunning)
-        {
-            var val = state->top;
-            val->tt = 3;
-            val->value.n = 1;
-            state->top += 1;
-            return 1;
-        }
-        else
-            return _enqueueSnipeTaskHook.Original.Invoke(scene, state);
-    }
-
-    public void Dispose()
-    {
-        _enqueueSnipeTaskHook.Dispose();
-    }
-}
diff --git a/Questionable/External/AutomatonIpc.cs b/Questionable/External/AutomatonIpc.cs
new file mode 100644 (file)
index 0000000..69e9467
--- /dev/null
@@ -0,0 +1,40 @@
+using Dalamud.Plugin;
+using Dalamud.Plugin.Ipc;
+using Dalamud.Plugin.Ipc.Exceptions;
+using Microsoft.Extensions.Logging;
+
+namespace Questionable.External;
+
+internal sealed class AutomatonIpc
+{
+    private readonly ILogger<AutomatonIpc> _logger;
+    private readonly ICallGateSubscriber<string,bool> _isTweakEnabled;
+    private bool _loggedIpcError;
+
+    public AutomatonIpc(IDalamudPluginInterface pluginInterface, ILogger<AutomatonIpc> logger)
+    {
+        _logger = logger;
+        _isTweakEnabled = pluginInterface.GetIpcSubscriber<string, bool>("Automaton.IsTweakEnabled");
+        logger.LogWarning("Automaton x {IsTweakEnabled}", IsAutoSnipeEnabled);
+    }
+
+    public bool IsAutoSnipeEnabled
+    {
+        get
+        {
+            try
+            {
+                return _isTweakEnabled.InvokeFunc("AutoSnipeQuests");
+            }
+            catch (IpcError e)
+            {
+                if (!_loggedIpcError)
+                {
+                    _loggedIpcError = true;
+                    _logger.LogWarning(e, "Could not query automaton for tweak status, probably not installed");
+                }
+                return false;
+            }
+        }
+    }
+}
index 7e9fb3f53bd5775e97bf140d00747a522b7d8023..8cc59d16e9fca2dc019eb4b2c00e781308316772 100644 (file)
@@ -112,7 +112,6 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         serviceCollection.AddSingleton<ChatFunctions>();
         serviceCollection.AddSingleton<QuestFunctions>();
         serviceCollection.AddSingleton<DalamudReflector>();
-        serviceCollection.AddSingleton<AutoSnipeHandler>();
 
         serviceCollection.AddSingleton<AetherCurrentData>();
         serviceCollection.AddSingleton<AetheryteData>();
@@ -128,6 +127,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         serviceCollection.AddSingleton<QuestionableIpc>();
         serviceCollection.AddSingleton<TextAdvanceIpc>();
         serviceCollection.AddSingleton<NotificationMasterIpc>();
+        serviceCollection.AddSingleton<AutomatonIpc>();
     }
 
     private static void AddTaskFactories(ServiceCollection serviceCollection)
@@ -300,8 +300,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         serviceProvider.GetRequiredService<ShopController>();
         serviceProvider.GetRequiredService<QuestionableIpc>();
         serviceProvider.GetRequiredService<DalamudInitializer>();
-        serviceProvider.GetRequiredService<AutoSnipeHandler>().Enable();
         serviceProvider.GetRequiredService<TextAdvanceIpc>();
+        serviceProvider.GetRequiredService<AutomatonIpc>();
     }
 
     public void Dispose()
index 97847de52c8126a397018a5c68640e2591b10b58..27c968a0f0a5ca0f23d8d4e925a8789beb054e02 100644 (file)
@@ -115,18 +115,6 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
             _configuration.General.ConfigureTextAdvance = configureTextAdvance;
             Save();
         }
-
-        if (ImGui.CollapsingHeader("Cheats"))
-        {
-            ImGui.TextColored(ImGuiColors.DalamudRed,
-                "This setting will be removed in a future version, and will be\navailable through TextAdvance instead.");
-            bool automaticallyCompleteSnipeTasks = _configuration.General.AutomaticallyCompleteSnipeTasks;
-            if (ImGui.Checkbox("Automatically complete snipe tasks", ref automaticallyCompleteSnipeTasks))
-            {
-                _configuration.General.AutomaticallyCompleteSnipeTasks = automaticallyCompleteSnipeTasks;
-                Save();
-            }
-        }
     }
 
     private void DrawNotificationsTab()
index 2ec22702b20a5f0f19f34da3b506a86a2616b92b..f1c715520ec9c3d504665dac9569244d4d38b155 100644 (file)
@@ -10,10 +10,11 @@ using ImGuiNET;
 using LLib;
 using LLib.ImGui;
 using Microsoft.Extensions.Logging;
+using Questionable.External;
 
 namespace Questionable.Windows;
 
-internal sealed class OneTimeSetupWindow : LWindow, IDisposable
+internal sealed class OneTimeSetupWindow : LWindow
 {
     private static readonly IReadOnlyList<PluginInfo> RequiredPlugins =
     [
@@ -22,35 +23,24 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
             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 Uri("https://github.com/awgil/ffxiv_navmesh/"),
+            new Uri("https://puni.sh/api/repository/veyn")),
         new("Lifestream",
             """
             Used to travel to aethernet shards in cities.
             """,
-            new Uri("https://github.com/NightmareXIV/Lifestream")),
+            new Uri("https://github.com/NightmareXIV/Lifestream"),
+            new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")),
         new("TextAdvance",
             """
             Automatically accepts and turns in quests, skips cutscenes
             and dialogue.
             """,
-            new Uri("https://github.com/NightmareXIV/TextAdvance")),
+            new Uri("https://github.com/NightmareXIV/TextAdvance"),
+            new Uri("https://github.com/NightmareXIV/MyDalamudPlugins/raw/main/pluginmaster.json")),
     ];
 
-    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")),
-        new("NotificationMaster",
-            """
-            Sends a configurable out-of-game notification if a quest
-            requires manual actions.
-            """,
-            new Uri("https://github.com/NightmareXIV/NotificationMaster")),
-    ];
+    private readonly IReadOnlyList<PluginInfo> _recommendedPlugins;
 
     private readonly Configuration _configuration;
     private readonly IDalamudPluginInterface _pluginInterface;
@@ -59,7 +49,7 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
     private readonly ILogger<OneTimeSetupWindow> _logger;
 
     public OneTimeSetupWindow(Configuration configuration, IDalamudPluginInterface pluginInterface, UiUtils uiUtils,
-        DalamudReflector dalamudReflector, ILogger<OneTimeSetupWindow> logger)
+        DalamudReflector dalamudReflector, ILogger<OneTimeSetupWindow> logger, AutomatonIpc automatonIpc)
         : base("Questionable Setup###QuestionableOneTimeSetup",
             ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings, true)
     {
@@ -69,6 +59,33 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
         _dalamudReflector = dalamudReflector;
         _logger = logger;
 
+        _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"),
+                new Uri(
+                    "https://raw.githubusercontent.com/FFXIV-CombatReborn/CombatRebornRepo/main/pluginmaster.json")),
+            new PluginInfo("Automaton",
+                """
+                Automaton is a collection of automation-related tweaks.
+                The 'Sniper no sniping' tweak can complete snipe tasks automatically.
+                """,
+                new Uri("https://github.com/Jaksuhn/Automaton"),
+                new Uri("https://puni.sh/api/repository/croizat"),
+                [new PluginDetailInfo("'Sniper no sniping' enabled", () => automatonIpc.IsAutoSnipeEnabled)]),
+            new("NotificationMaster",
+                """
+                Sends a configurable out-of-game notification if a quest
+                requires manual actions.
+                """,
+                new Uri("https://github.com/NightmareXIV/NotificationMaster"),
+                null),
+        ];
+
         RespectCloseHotkey = false;
         ShowCloseButton = false;
         AllowPinning = false;
@@ -101,7 +118,7 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
         ImGui.Text("The following plugins are recommended, but not required:");
         using (ImRaii.PushIndent())
         {
-            foreach (var plugin in RecommendedPlugins)
+            foreach (var plugin in _recommendedPlugins)
                 DrawPlugin(plugin, checklistPadding);
         }
 
@@ -149,8 +166,28 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
             using (ImRaii.PushIndent(checklistPadding))
             {
                 ImGui.TextUnformatted(plugin.Details);
-                if (!isInstalled && ImGui.Button("Open Repository"))
-                    Util.OpenLink(plugin.Uri.ToString());
+                if (plugin.DetailsToCheck != null)
+                {
+                    foreach (var detail in plugin.DetailsToCheck)
+                        _uiUtils.ChecklistItem(detail.DisplayName, isInstalled && detail.Predicate());
+                }
+
+                ImGui.Spacing();
+
+                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");
+                }
             }
         }
 
@@ -162,12 +199,12 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
         return _dalamudReflector.TryGetDalamudPlugin(internalName, out _, suppressErrors: true, ignoreCache: true);
     }
 
-    public void Dispose()
-    {
-    }
-
     private sealed record PluginInfo(
         string DisplayName,
         string Details,
-        Uri Uri);
+        Uri WebsiteUri,
+        Uri? DalamudRepositoryUri,
+        List<PluginDetailInfo>? DetailsToCheck = null);
+
+    private sealed record PluginDetailInfo(string DisplayName, Func<bool> Predicate);
 }