diff --git a/AutoFishingStool/AutoFishingStool.csproj b/AutoFishingStool/AutoFishingStool.csproj index 97b8922..1e51ad0 100644 --- a/AutoFishingStool/AutoFishingStool.csproj +++ b/AutoFishingStool/AutoFishingStool.csproj @@ -1,9 +1,9 @@ - 1.1.3.0 + 1.2.0.0 Bep - Automatically uses /sit after fishing becomes ready. + Automatically uses /sit while fishing, with an option to stand during Spectral Currents. MIT false true diff --git a/AutoFishingStool/AutoFishingStool.json b/AutoFishingStool/AutoFishingStool.json index fa1affb..57a3ec2 100644 --- a/AutoFishingStool/AutoFishingStool.json +++ b/AutoFishingStool/AutoFishingStool.json @@ -2,7 +2,7 @@ "Author": "Bep", "Name": "Auto Fishing Stool", "Punchline": "Automatically sits after entering fishing mode.", - "Description": "Uses /sit once after fishing becomes active and the current fishing action finishes.", + "Description": "Automatically uses /sit while fishing, with an option to stand during Spectral Currents.", "ApplicableVersion": "any", "RepoUrl": "https://git.bep.moe/verniy/xiv-plugins", "IconUrl": "https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/AutoFishingStool/icon.png", diff --git a/AutoFishingStool/Configuration.cs b/AutoFishingStool/Configuration.cs new file mode 100644 index 0000000..696121e --- /dev/null +++ b/AutoFishingStool/Configuration.cs @@ -0,0 +1,10 @@ +using Dalamud.Configuration; + +namespace AutoFishingStool; + +public sealed class Configuration : IPluginConfiguration +{ + public int Version { get; set; } = 1; + + public bool StandDuringSpectralCurrents { get; set; } = true; +} diff --git a/AutoFishingStool/Plugin.cs b/AutoFishingStool/Plugin.cs index e89fc44..aec229e 100644 --- a/AutoFishingStool/Plugin.cs +++ b/AutoFishingStool/Plugin.cs @@ -1,8 +1,12 @@ using System; +using System.Numerics; +using Dalamud.Bindings.ImGui; +using Dalamud.Configuration; using Dalamud.Game.ClientState.Conditions; using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game.Event; using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.UI; @@ -10,10 +14,20 @@ namespace AutoFishingStool; public sealed unsafe class Plugin : IDalamudPlugin { + private enum PostureAction + { + None, + Sit, + Stand, + } + private const int SitDelayMilliseconds = 2500; private const int ActionSettleMilliseconds = 500; private const uint FisherClassJobId = 18; + [PluginService] + internal static IDalamudPluginInterface PluginInterface { get; private set; } = null!; + [PluginService] internal static ICondition Condition { get; private set; } = null!; @@ -26,14 +40,25 @@ public sealed unsafe class Plugin : IDalamudPlugin [PluginService] internal static IPluginLog Log { get; private set; } = null!; - private DateTime? sitAt; - private bool satThisSession; + private readonly Configuration configuration; + private DateTime? postureAt; + private PostureAction pendingPosture; + private bool satByPlugin; + private bool stoodForSpectral; + private bool spectralCurrentActive; private bool waitingForActionToFinish; + private bool configWindowOpen; public Plugin() { + configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration(); + spectralCurrentActive = IsSpectralCurrentActive(); + Condition.ConditionChange += OnConditionChange; Framework.Update += OnFrameworkUpdate; + PluginInterface.UiBuilder.Draw += DrawConfigWindow; + PluginInterface.UiBuilder.OpenConfigUi += OpenConfigWindow; + PluginInterface.UiBuilder.OpenMainUi += OpenConfigWindow; Log.Information("Auto Fishing Stool loaded"); } @@ -43,16 +68,14 @@ public sealed unsafe class Plugin : IDalamudPlugin { Log.Information("Fishing condition changed to {Value}", value); - if (value && Condition[ConditionFlag.Gathering] && IsFisher() && !satThisSession) + if (value && Condition[ConditionFlag.Gathering] && IsFisher() && !satByPlugin && !ShouldStandForSpectral()) { - sitAt = DateTime.UtcNow.AddMilliseconds(SitDelayMilliseconds); - waitingForActionToFinish = false; + SchedulePosture(PostureAction.Sit, SitDelayMilliseconds); Log.Information("Fishing became active; stable Sit timer started"); } - else if (!value && !satThisSession) + else if (!value && !satByPlugin) { - sitAt = null; - waitingForActionToFinish = false; + CancelPendingPosture(); Log.Information("Fishing became inactive; stable Sit timer reset"); } @@ -69,32 +92,44 @@ public sealed unsafe class Plugin : IDalamudPlugin if (!IsFisher()) return; - satThisSession = false; - sitAt = null; - waitingForActionToFinish = false; + satByPlugin = false; + stoodForSpectral = false; + CancelPendingPosture(); Log.Information("Fisher gathering session started"); return; } - sitAt = null; - satThisSession = false; - waitingForActionToFinish = false; + satByPlugin = false; + stoodForSpectral = false; + CancelPendingPosture(); } private void OnFrameworkUpdate(IFramework framework) { - if (satThisSession || sitAt is null) + UpdateSpectralCurrentState(); + + if (pendingPosture == PostureAction.None || postureAt is null) return; if (!Condition[ConditionFlag.Gathering] || !Condition[ConditionFlag.Fishing] || !IsFisher()) { - sitAt = null; - satThisSession = false; - waitingForActionToFinish = false; + CancelPendingPosture(); return; } - if (DateTime.UtcNow < sitAt.Value) + if (pendingPosture == PostureAction.Sit && ShouldStandForSpectral()) + { + CancelPendingPosture(); + return; + } + + if (pendingPosture == PostureAction.Stand && !ShouldStandForSpectral()) + { + CancelPendingPosture(); + return; + } + + if (DateTime.UtcNow < postureAt.Value) return; if (Condition[ConditionFlag.ExecutingGatheringAction]) @@ -111,16 +146,18 @@ public sealed unsafe class Plugin : IDalamudPlugin if (waitingForActionToFinish) { waitingForActionToFinish = false; - sitAt = DateTime.UtcNow.AddMilliseconds(ActionSettleMilliseconds); - Log.Information("Fishing action finished; Sit command scheduled after settling"); + postureAt = DateTime.UtcNow.AddMilliseconds(ActionSettleMilliseconds); + Log.Information("Fishing action finished; posture command scheduled after settling"); return; } + var completedPosture = pendingPosture; + var uiModule = UIModule.Instance(); if (uiModule is null) { Log.Warning("Could not access the game's UI module"); - sitAt = null; + CancelPendingPosture(); return; } @@ -128,17 +165,142 @@ public sealed unsafe class Plugin : IDalamudPlugin try { uiModule->ProcessChatBoxEntry(command); - Log.Information("Submitted /sit after fishing started"); + Log.Information("Submitted /sit to {Posture}", completedPosture); } finally { command->Dtor(true); } - satThisSession = true; - sitAt = null; + + if (completedPosture == PostureAction.Sit) + { + satByPlugin = true; + stoodForSpectral = false; + } + else + { + satByPlugin = false; + stoodForSpectral = true; + } + + CancelPendingPosture(); + } + + private void UpdateSpectralCurrentState() + { + var active = IsSpectralCurrentActive(); + if (active == spectralCurrentActive) + return; + + spectralCurrentActive = active; + Log.Information("Spectral Current changed to {Value}", active); + + if (!configuration.StandDuringSpectralCurrents) + return; + + if (active) + { + if (pendingPosture == PostureAction.Sit) + CancelPendingPosture(); + + if (satByPlugin) + SchedulePosture(PostureAction.Stand, 0); + + return; + } + + if (stoodForSpectral && Condition[ConditionFlag.Gathering] && Condition[ConditionFlag.Fishing] && IsFisher()) + SchedulePosture(PostureAction.Sit, ActionSettleMilliseconds); + } + + private static bool IsSpectralCurrentActive() + { + try + { + var eventFramework = EventFramework.Instance(); + var oceanFishing = eventFramework is null ? null : eventFramework->GetInstanceContentOceanFishing(); + return oceanFishing is not null && oceanFishing->SpectralCurrentActive; + } + catch (Exception exception) + { + Log.Warning(exception, "Could not read Ocean Fishing state"); + return false; + } + } + + private bool ShouldStandForSpectral() + { + return configuration.StandDuringSpectralCurrents && spectralCurrentActive; + } + + private void SchedulePosture(PostureAction posture, int delayMilliseconds) + { + pendingPosture = posture; + postureAt = DateTime.UtcNow.AddMilliseconds(delayMilliseconds); waitingForActionToFinish = false; } + private void CancelPendingPosture() + { + pendingPosture = PostureAction.None; + postureAt = null; + waitingForActionToFinish = false; + } + + private void OpenConfigWindow() + { + configWindowOpen = true; + } + + private void DrawConfigWindow() + { + if (!configWindowOpen) + return; + + ImGui.SetNextWindowSize(new Vector2(430, 0), ImGuiCond.FirstUseEver); + if (!ImGui.Begin("Auto Fishing Stool Settings###AutoFishingStoolSettings", ref configWindowOpen, ImGuiWindowFlags.AlwaysAutoResize)) + { + ImGui.End(); + return; + } + + var standDuringSpectralCurrents = configuration.StandDuringSpectralCurrents; + if (ImGui.Checkbox("Stand during Spectral Currents", ref standDuringSpectralCurrents)) + { + configuration.StandDuringSpectralCurrents = standDuringSpectralCurrents; + PluginInterface.SavePluginConfig(configuration); + ApplySpectralSettingChange(); + } + + ImGui.TextWrapped("Stand while a Spectral Current is active, then sit again when it ends."); + ImGui.Separator(); + ImGui.Text($"Spectral Current detected: {(spectralCurrentActive ? "Yes" : "No")}"); + ImGui.End(); + } + + private void ApplySpectralSettingChange() + { + if (!spectralCurrentActive) + return; + + if (configuration.StandDuringSpectralCurrents) + { + if (pendingPosture == PostureAction.Sit) + CancelPendingPosture(); + + if (satByPlugin) + SchedulePosture(PostureAction.Stand, 0); + + return; + } + + if (pendingPosture == PostureAction.Stand) + CancelPendingPosture(); + + if (stoodForSpectral && Condition[ConditionFlag.Gathering] && Condition[ConditionFlag.Fishing] && IsFisher()) + SchedulePosture(PostureAction.Sit, ActionSettleMilliseconds); + } + private static bool IsFisher() { return PlayerState.IsLoaded && PlayerState.ClassJob.RowId == FisherClassJobId; @@ -148,5 +310,8 @@ public sealed unsafe class Plugin : IDalamudPlugin { Condition.ConditionChange -= OnConditionChange; Framework.Update -= OnFrameworkUpdate; + PluginInterface.UiBuilder.Draw -= DrawConfigWindow; + PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigWindow; + PluginInterface.UiBuilder.OpenMainUi -= OpenConfigWindow; } } diff --git a/pluginmaster.json b/pluginmaster.json index aafa731..dd89b01 100644 --- a/pluginmaster.json +++ b/pluginmaster.json @@ -3,9 +3,9 @@ "Author": "Bep", "Name": "Auto Fishing Stool", "InternalName": "AutoFishingStool", - "AssemblyVersion": "1.1.3.0", + "AssemblyVersion": "1.2.0.0", "TestingAssemblyVersion": null, - "Description": "Uses /sit once after fishing becomes active and the current fishing action finishes.", + "Description": "Automatically uses /sit while fishing, with an option to stand during Spectral Currents.", "Punchline": "Automatically sits after entering fishing mode.", "RepoUrl": "https://git.bep.moe/verniy/xiv-plugins", "ApplicableVersion": "any", @@ -14,12 +14,12 @@ "IsTestingExclusive": false, "DownloadLinkInstall": "https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/repo/AutoFishingStool/latest.zip", "DownloadLinkUpdate": "https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/repo/AutoFishingStool/latest.zip", - "LastUpdate": "1786068591", + "LastUpdate": "1786130579", "IconUrl": "https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/AutoFishingStool/icon.png", "Tags": [ "fishing", "utility" ], - "Changelog": "Initial custom repository release." + "Changelog": "Add a setting to stand during Spectral Currents and sit again afterward." } ] diff --git a/repo/AutoFishingStool/latest.zip b/repo/AutoFishingStool/latest.zip index d9f9def..d450fb4 100644 Binary files a/repo/AutoFishingStool/latest.zip and b/repo/AutoFishingStool/latest.zip differ