1
0
forked from liza/Questionable

Remove 'Automatically complete snipe tasks'

This commit is contained in:
Liza 2024-11-22 21:52:35 +01:00
parent 73e030b620
commit 0265fcc0ae
Signed by: liza
GPG Key ID: 7199F8D727D55F67
8 changed files with 113 additions and 96 deletions

View File

@ -7,7 +7,7 @@ namespace Questionable;
internal sealed class Configuration : IPluginConfiguration internal sealed class Configuration : IPluginConfiguration
{ {
public const int PluginSetupVersion = 1; public const int PluginSetupVersion = 2;
public int Version { get; set; } =1 ; public int Version { get; set; } =1 ;
public int PluginSetupCompleteVersion { get; set; } public int PluginSetupCompleteVersion { get; set; }
@ -28,7 +28,6 @@ internal sealed class Configuration : IPluginConfiguration
public bool HideInAllInstances { get; set; } = true; public bool HideInAllInstances { get; set; } = true;
public bool UseEscToCancelQuesting { get; set; } = true; public bool UseEscToCancelQuesting { get; set; } = true;
public bool ShowIncompleteSeasonalEvents { get; set; } = true; public bool ShowIncompleteSeasonalEvents { get; set; } = true;
public bool AutomaticallyCompleteSnipeTasks { get; set; }
public bool ConfigureTextAdvance { get; set; } = true; public bool ConfigureTextAdvance { get; set; } = true;
} }

View File

@ -12,14 +12,14 @@ namespace Questionable.Controller.Steps.Common;
internal static class SendNotification internal static class SendNotification
{ {
internal sealed class Factory( internal sealed class Factory(
Configuration configuration, AutomatonIpc automatonIpc,
TerritoryData territoryData) : SimpleTaskFactory TerritoryData territoryData) : SimpleTaskFactory
{ {
public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step) public override ITask? CreateTask(Quest quest, QuestSequence sequence, QuestStep step)
{ {
return step.InteractionType switch return step.InteractionType switch
{ {
EInteractionType.Snipe when !configuration.General.AutomaticallyCompleteSnipeTasks => EInteractionType.Snipe when !automatonIpc.IsAutoSnipeEnabled =>
new Task(step.InteractionType, step.Comment), new Task(step.InteractionType, step.Comment),
EInteractionType.Duty => EInteractionType.Duty =>
new Task(step.InteractionType, step.ContentFinderConditionId.HasValue new Task(step.InteractionType, step.ContentFinderConditionId.HasValue

View File

@ -8,6 +8,7 @@ using FFXIVClientStructs.FFXIV.Client.Game;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Questionable.Controller.Steps.Shared; using Questionable.Controller.Steps.Shared;
using Questionable.Controller.Utils; using Questionable.Controller.Utils;
using Questionable.External;
using Questionable.Functions; using Questionable.Functions;
using Questionable.Model; using Questionable.Model;
using Questionable.Model.Questing; using Questionable.Model.Questing;
@ -16,7 +17,7 @@ namespace Questionable.Controller.Steps.Interactions;
internal static class Interact 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) public IEnumerable<ITask> CreateAllTasks(Quest quest, QuestSequence sequence, QuestStep step)
{ {
@ -43,7 +44,7 @@ internal static class Interact
} }
else if (step.InteractionType == EInteractionType.Snipe) else if (step.InteractionType == EInteractionType.Snipe)
{ {
if (!configuration.General.AutomaticallyCompleteSnipeTasks) if (!automatonIpc.IsAutoSnipeEnabled)
yield break; yield break;
} }
else if (step.InteractionType != EInteractionType.Interact) else if (step.InteractionType != EInteractionType.Interact)

View File

@ -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();
}
}

40
Questionable/External/AutomatonIpc.cs vendored Normal file
View File

@ -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;
}
}
}
}

View File

@ -112,7 +112,6 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton<ChatFunctions>(); serviceCollection.AddSingleton<ChatFunctions>();
serviceCollection.AddSingleton<QuestFunctions>(); serviceCollection.AddSingleton<QuestFunctions>();
serviceCollection.AddSingleton<DalamudReflector>(); serviceCollection.AddSingleton<DalamudReflector>();
serviceCollection.AddSingleton<AutoSnipeHandler>();
serviceCollection.AddSingleton<AetherCurrentData>(); serviceCollection.AddSingleton<AetherCurrentData>();
serviceCollection.AddSingleton<AetheryteData>(); serviceCollection.AddSingleton<AetheryteData>();
@ -128,6 +127,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceCollection.AddSingleton<QuestionableIpc>(); serviceCollection.AddSingleton<QuestionableIpc>();
serviceCollection.AddSingleton<TextAdvanceIpc>(); serviceCollection.AddSingleton<TextAdvanceIpc>();
serviceCollection.AddSingleton<NotificationMasterIpc>(); serviceCollection.AddSingleton<NotificationMasterIpc>();
serviceCollection.AddSingleton<AutomatonIpc>();
} }
private static void AddTaskFactories(ServiceCollection serviceCollection) private static void AddTaskFactories(ServiceCollection serviceCollection)
@ -300,8 +300,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin
serviceProvider.GetRequiredService<ShopController>(); serviceProvider.GetRequiredService<ShopController>();
serviceProvider.GetRequiredService<QuestionableIpc>(); serviceProvider.GetRequiredService<QuestionableIpc>();
serviceProvider.GetRequiredService<DalamudInitializer>(); serviceProvider.GetRequiredService<DalamudInitializer>();
serviceProvider.GetRequiredService<AutoSnipeHandler>().Enable();
serviceProvider.GetRequiredService<TextAdvanceIpc>(); serviceProvider.GetRequiredService<TextAdvanceIpc>();
serviceProvider.GetRequiredService<AutomatonIpc>();
} }
public void Dispose() public void Dispose()

View File

@ -115,18 +115,6 @@ internal sealed class ConfigWindow : LWindow, IPersistableWindowConfig
_configuration.General.ConfigureTextAdvance = configureTextAdvance; _configuration.General.ConfigureTextAdvance = configureTextAdvance;
Save(); 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() private void DrawNotificationsTab()

View File

@ -10,10 +10,11 @@ using ImGuiNET;
using LLib; using LLib;
using LLib.ImGui; using LLib.ImGui;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Questionable.External;
namespace Questionable.Windows; namespace Questionable.Windows;
internal sealed class OneTimeSetupWindow : LWindow, IDisposable internal sealed class OneTimeSetupWindow : LWindow
{ {
private static readonly IReadOnlyList<PluginInfo> RequiredPlugins = private static readonly IReadOnlyList<PluginInfo> RequiredPlugins =
[ [
@ -22,35 +23,24 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
vnavmesh handles the navigation within a zone, moving vnavmesh handles the navigation within a zone, moving
your character to the next quest-related objective. 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", new("Lifestream",
""" """
Used to travel to aethernet shards in cities. 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", new("TextAdvance",
""" """
Automatically accepts and turns in quests, skips cutscenes Automatically accepts and turns in quests, skips cutscenes
and dialogue. 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 = private 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 Configuration _configuration; private readonly Configuration _configuration;
private readonly IDalamudPluginInterface _pluginInterface; private readonly IDalamudPluginInterface _pluginInterface;
@ -59,7 +49,7 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
private readonly ILogger<OneTimeSetupWindow> _logger; private readonly ILogger<OneTimeSetupWindow> _logger;
public OneTimeSetupWindow(Configuration configuration, IDalamudPluginInterface pluginInterface, UiUtils uiUtils, public OneTimeSetupWindow(Configuration configuration, IDalamudPluginInterface pluginInterface, UiUtils uiUtils,
DalamudReflector dalamudReflector, ILogger<OneTimeSetupWindow> logger) DalamudReflector dalamudReflector, ILogger<OneTimeSetupWindow> logger, AutomatonIpc automatonIpc)
: base("Questionable Setup###QuestionableOneTimeSetup", : base("Questionable Setup###QuestionableOneTimeSetup",
ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings, true) ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings, true)
{ {
@ -69,6 +59,33 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
_dalamudReflector = dalamudReflector; _dalamudReflector = dalamudReflector;
_logger = logger; _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; RespectCloseHotkey = false;
ShowCloseButton = false; ShowCloseButton = false;
AllowPinning = false; AllowPinning = false;
@ -101,7 +118,7 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
ImGui.Text("The following plugins are recommended, but not required:"); ImGui.Text("The following plugins are recommended, but not required:");
using (ImRaii.PushIndent()) using (ImRaii.PushIndent())
{ {
foreach (var plugin in RecommendedPlugins) foreach (var plugin in _recommendedPlugins)
DrawPlugin(plugin, checklistPadding); DrawPlugin(plugin, checklistPadding);
} }
@ -149,8 +166,28 @@ internal sealed class OneTimeSetupWindow : LWindow, IDisposable
using (ImRaii.PushIndent(checklistPadding)) using (ImRaii.PushIndent(checklistPadding))
{ {
ImGui.TextUnformatted(plugin.Details); ImGui.TextUnformatted(plugin.Details);
if (!isInstalled && ImGui.Button("Open Repository")) if (plugin.DetailsToCheck != null)
Util.OpenLink(plugin.Uri.ToString()); {
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); return _dalamudReflector.TryGetDalamudPlugin(internalName, out _, suppressErrors: true, ignoreCache: true);
} }
public void Dispose()
{
}
private sealed record PluginInfo( private sealed record PluginInfo(
string DisplayName, string DisplayName,
string Details, string Details,
Uri Uri); Uri WebsiteUri,
Uri? DalamudRepositoryUri,
List<PluginDetailInfo>? DetailsToCheck = null);
private sealed record PluginDetailInfo(string DisplayName, Func<bool> Predicate);
} }