Compare commits

...

5 Commits

Author SHA1 Message Date
6a952b5d8d Update README.md 2026-08-07 22:11:39 +00:00
1c0cda0c9b chore: update plugin punchline 2026-08-07 15:50:48 -04:00
1f8ff12dc1 feat: stand during spectral currents 2026-08-07 15:23:19 -04:00
fa3c4078b6 docs: hide local publishing workflow 2026-08-06 22:13:27 -04:00
31fec9335c feat: add Dalamud custom plugin repository 2026-08-06 22:10:07 -04:00
7 changed files with 238 additions and 30 deletions

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Dalamud.NET.Sdk/15.0.0"> <Project Sdk="Dalamud.NET.Sdk/15.0.0">
<PropertyGroup> <PropertyGroup>
<Version>1.1.3.0</Version> <Version>1.2.0.1</Version>
<Authors>Bep</Authors> <Authors>Bep</Authors>
<Description>Automatically uses /sit after fishing becomes ready.</Description> <Description>Automatically uses /sit while fishing, with an option to stand during Spectral Currents.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>

View File

@@ -1,8 +1,8 @@
{ {
"Author": "Bep", "Author": "Bep",
"Name": "Auto Fishing Stool", "Name": "Auto Fishing Stool",
"Punchline": "Automatically sits after entering fishing mode.", "Punchline": "Automatically sits while fishing.",
"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", "ApplicableVersion": "any",
"RepoUrl": "https://git.bep.moe/verniy/xiv-plugins", "RepoUrl": "https://git.bep.moe/verniy/xiv-plugins",
"IconUrl": "https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/AutoFishingStool/icon.png", "IconUrl": "https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/AutoFishingStool/icon.png",

View File

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

View File

@@ -1,8 +1,12 @@
using System; using System;
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Configuration;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
using Dalamud.IoC; using Dalamud.IoC;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Event;
using FFXIVClientStructs.FFXIV.Client.System.String; using FFXIVClientStructs.FFXIV.Client.System.String;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
@@ -10,10 +14,20 @@ namespace AutoFishingStool;
public sealed unsafe class Plugin : IDalamudPlugin public sealed unsafe class Plugin : IDalamudPlugin
{ {
private enum PostureAction
{
None,
Sit,
Stand,
}
private const int SitDelayMilliseconds = 2500; private const int SitDelayMilliseconds = 2500;
private const int ActionSettleMilliseconds = 500; private const int ActionSettleMilliseconds = 500;
private const uint FisherClassJobId = 18; private const uint FisherClassJobId = 18;
[PluginService]
internal static IDalamudPluginInterface PluginInterface { get; private set; } = null!;
[PluginService] [PluginService]
internal static ICondition Condition { get; private set; } = null!; internal static ICondition Condition { get; private set; } = null!;
@@ -26,14 +40,25 @@ public sealed unsafe class Plugin : IDalamudPlugin
[PluginService] [PluginService]
internal static IPluginLog Log { get; private set; } = null!; internal static IPluginLog Log { get; private set; } = null!;
private DateTime? sitAt; private readonly Configuration configuration;
private bool satThisSession; private DateTime? postureAt;
private PostureAction pendingPosture;
private bool satByPlugin;
private bool stoodForSpectral;
private bool spectralCurrentActive;
private bool waitingForActionToFinish; private bool waitingForActionToFinish;
private bool configWindowOpen;
public Plugin() public Plugin()
{ {
configuration = PluginInterface.GetPluginConfig() as Configuration ?? new Configuration();
spectralCurrentActive = IsSpectralCurrentActive();
Condition.ConditionChange += OnConditionChange; Condition.ConditionChange += OnConditionChange;
Framework.Update += OnFrameworkUpdate; Framework.Update += OnFrameworkUpdate;
PluginInterface.UiBuilder.Draw += DrawConfigWindow;
PluginInterface.UiBuilder.OpenConfigUi += OpenConfigWindow;
PluginInterface.UiBuilder.OpenMainUi += OpenConfigWindow;
Log.Information("Auto Fishing Stool loaded"); Log.Information("Auto Fishing Stool loaded");
} }
@@ -43,16 +68,14 @@ public sealed unsafe class Plugin : IDalamudPlugin
{ {
Log.Information("Fishing condition changed to {Value}", value); 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); SchedulePosture(PostureAction.Sit, SitDelayMilliseconds);
waitingForActionToFinish = false;
Log.Information("Fishing became active; stable Sit timer started"); Log.Information("Fishing became active; stable Sit timer started");
} }
else if (!value && !satThisSession) else if (!value && !satByPlugin)
{ {
sitAt = null; CancelPendingPosture();
waitingForActionToFinish = false;
Log.Information("Fishing became inactive; stable Sit timer reset"); Log.Information("Fishing became inactive; stable Sit timer reset");
} }
@@ -69,32 +92,44 @@ public sealed unsafe class Plugin : IDalamudPlugin
if (!IsFisher()) if (!IsFisher())
return; return;
satThisSession = false; satByPlugin = false;
sitAt = null; stoodForSpectral = false;
waitingForActionToFinish = false; CancelPendingPosture();
Log.Information("Fisher gathering session started"); Log.Information("Fisher gathering session started");
return; return;
} }
sitAt = null; satByPlugin = false;
satThisSession = false; stoodForSpectral = false;
waitingForActionToFinish = false; CancelPendingPosture();
} }
private void OnFrameworkUpdate(IFramework framework) private void OnFrameworkUpdate(IFramework framework)
{ {
if (satThisSession || sitAt is null) UpdateSpectralCurrentState();
if (pendingPosture == PostureAction.None || postureAt is null)
return; return;
if (!Condition[ConditionFlag.Gathering] || !Condition[ConditionFlag.Fishing] || !IsFisher()) if (!Condition[ConditionFlag.Gathering] || !Condition[ConditionFlag.Fishing] || !IsFisher())
{ {
sitAt = null; CancelPendingPosture();
satThisSession = false;
waitingForActionToFinish = false;
return; 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; return;
if (Condition[ConditionFlag.ExecutingGatheringAction]) if (Condition[ConditionFlag.ExecutingGatheringAction])
@@ -111,16 +146,18 @@ public sealed unsafe class Plugin : IDalamudPlugin
if (waitingForActionToFinish) if (waitingForActionToFinish)
{ {
waitingForActionToFinish = false; waitingForActionToFinish = false;
sitAt = DateTime.UtcNow.AddMilliseconds(ActionSettleMilliseconds); postureAt = DateTime.UtcNow.AddMilliseconds(ActionSettleMilliseconds);
Log.Information("Fishing action finished; Sit command scheduled after settling"); Log.Information("Fishing action finished; posture command scheduled after settling");
return; return;
} }
var completedPosture = pendingPosture;
var uiModule = UIModule.Instance(); var uiModule = UIModule.Instance();
if (uiModule is null) if (uiModule is null)
{ {
Log.Warning("Could not access the game's UI module"); Log.Warning("Could not access the game's UI module");
sitAt = null; CancelPendingPosture();
return; return;
} }
@@ -128,17 +165,142 @@ public sealed unsafe class Plugin : IDalamudPlugin
try try
{ {
uiModule->ProcessChatBoxEntry(command); uiModule->ProcessChatBoxEntry(command);
Log.Information("Submitted /sit after fishing started"); Log.Information("Submitted /sit to {Posture}", completedPosture);
} }
finally finally
{ {
command->Dtor(true); 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; 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() private static bool IsFisher()
{ {
return PlayerState.IsLoaded && PlayerState.ClassJob.RowId == FisherClassJobId; return PlayerState.IsLoaded && PlayerState.ClassJob.RowId == FisherClassJobId;
@@ -148,5 +310,8 @@ public sealed unsafe class Plugin : IDalamudPlugin
{ {
Condition.ConditionChange -= OnConditionChange; Condition.ConditionChange -= OnConditionChange;
Framework.Update -= OnFrameworkUpdate; Framework.Update -= OnFrameworkUpdate;
PluginInterface.UiBuilder.Draw -= DrawConfigWindow;
PluginInterface.UiBuilder.OpenConfigUi -= OpenConfigWindow;
PluginInterface.UiBuilder.OpenMainUi -= OpenConfigWindow;
} }
} }

View File

@@ -2,6 +2,14 @@
Small Dalamud plugins by Bep. Small Dalamud plugins by Bep.
## Dalamud Custom Repository
Add this URL under Dalamud Settings, Experimental, Custom Plugin Repositories:
```text
https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/pluginmaster.json
```
## Plugins ## Plugins
- [Auto Fishing Stool](AutoFishingStool/README.md): automatically uses `/sit` after entering fishing mode. - [Auto Fishing Stool](AutoFishingStool/README.md): automatically uses `/sit` after starting to fish

25
pluginmaster.json Normal file
View File

@@ -0,0 +1,25 @@
[
{
"Author": "Bep",
"Name": "Auto Fishing Stool",
"InternalName": "AutoFishingStool",
"AssemblyVersion": "1.2.0.1",
"TestingAssemblyVersion": null,
"Description": "Automatically uses /sit while fishing, with an option to stand during Spectral Currents.",
"Punchline": "Automatically sits while fishing.",
"RepoUrl": "https://git.bep.moe/verniy/xiv-plugins",
"ApplicableVersion": "any",
"DalamudApiLevel": 15,
"IsHide": false,
"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": "1786132230",
"IconUrl": "https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/AutoFishingStool/icon.png",
"Tags": [
"fishing",
"utility"
],
"Changelog": "Update the plugin punchline."
}
]

Binary file not shown.