feat: add Auto Fishing Stool plugin
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
**/bin/
|
||||
**/obj/
|
||||
.vs/
|
||||
*.user
|
||||
17
AutoFishingStool/AutoFishingStool.csproj
Normal file
17
AutoFishingStool/AutoFishingStool.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Dalamud.NET.Sdk/15.0.0">
|
||||
<PropertyGroup>
|
||||
<Version>1.1.3.0</Version>
|
||||
<Authors>Bep</Authors>
|
||||
<Description>Automatically uses /sit after fishing becomes ready.</Description>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="icon.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
13
AutoFishingStool/AutoFishingStool.json
Normal file
13
AutoFishingStool/AutoFishingStool.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"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.",
|
||||
"ApplicableVersion": "any",
|
||||
"RepoUrl": "https://git.bep.moe/verniy/xiv-plugins",
|
||||
"IconUrl": "https://git.bep.moe/verniy/xiv-plugins/raw/branch/main/AutoFishingStool/icon.png",
|
||||
"Tags": [
|
||||
"fishing",
|
||||
"utility"
|
||||
]
|
||||
}
|
||||
152
AutoFishingStool/Plugin.cs
Normal file
152
AutoFishingStool/Plugin.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
|
||||
namespace AutoFishingStool;
|
||||
|
||||
public sealed unsafe class Plugin : IDalamudPlugin
|
||||
{
|
||||
private const int SitDelayMilliseconds = 2500;
|
||||
private const int ActionSettleMilliseconds = 500;
|
||||
private const uint FisherClassJobId = 18;
|
||||
|
||||
[PluginService]
|
||||
internal static ICondition Condition { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal static IFramework Framework { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal static IPlayerState PlayerState { get; private set; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal static IPluginLog Log { get; private set; } = null!;
|
||||
|
||||
private DateTime? sitAt;
|
||||
private bool satThisSession;
|
||||
private bool waitingForActionToFinish;
|
||||
|
||||
public Plugin()
|
||||
{
|
||||
Condition.ConditionChange += OnConditionChange;
|
||||
Framework.Update += OnFrameworkUpdate;
|
||||
Log.Information("Auto Fishing Stool loaded");
|
||||
}
|
||||
|
||||
private void OnConditionChange(ConditionFlag flag, bool value)
|
||||
{
|
||||
if (flag == ConditionFlag.Fishing)
|
||||
{
|
||||
Log.Information("Fishing condition changed to {Value}", value);
|
||||
|
||||
if (value && Condition[ConditionFlag.Gathering] && IsFisher() && !satThisSession)
|
||||
{
|
||||
sitAt = DateTime.UtcNow.AddMilliseconds(SitDelayMilliseconds);
|
||||
waitingForActionToFinish = false;
|
||||
Log.Information("Fishing became active; stable Sit timer started");
|
||||
}
|
||||
else if (!value && !satThisSession)
|
||||
{
|
||||
sitAt = null;
|
||||
waitingForActionToFinish = false;
|
||||
Log.Information("Fishing became inactive; stable Sit timer reset");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (flag != ConditionFlag.Gathering)
|
||||
return;
|
||||
|
||||
Log.Information("Gathering condition changed to {Value}", value);
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (!IsFisher())
|
||||
return;
|
||||
|
||||
satThisSession = false;
|
||||
sitAt = null;
|
||||
waitingForActionToFinish = false;
|
||||
Log.Information("Fisher gathering session started");
|
||||
return;
|
||||
}
|
||||
|
||||
sitAt = null;
|
||||
satThisSession = false;
|
||||
waitingForActionToFinish = false;
|
||||
}
|
||||
|
||||
private void OnFrameworkUpdate(IFramework framework)
|
||||
{
|
||||
if (satThisSession || sitAt is null)
|
||||
return;
|
||||
|
||||
if (!Condition[ConditionFlag.Gathering] || !Condition[ConditionFlag.Fishing] || !IsFisher())
|
||||
{
|
||||
sitAt = null;
|
||||
satThisSession = false;
|
||||
waitingForActionToFinish = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow < sitAt.Value)
|
||||
return;
|
||||
|
||||
if (Condition[ConditionFlag.ExecutingGatheringAction])
|
||||
{
|
||||
if (!waitingForActionToFinish)
|
||||
{
|
||||
waitingForActionToFinish = true;
|
||||
Log.Information("Sit timer elapsed during a fishing action; waiting for it to finish");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (waitingForActionToFinish)
|
||||
{
|
||||
waitingForActionToFinish = false;
|
||||
sitAt = DateTime.UtcNow.AddMilliseconds(ActionSettleMilliseconds);
|
||||
Log.Information("Fishing action finished; Sit command scheduled after settling");
|
||||
return;
|
||||
}
|
||||
|
||||
var uiModule = UIModule.Instance();
|
||||
if (uiModule is null)
|
||||
{
|
||||
Log.Warning("Could not access the game's UI module");
|
||||
sitAt = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var command = Utf8String.FromString("/sit");
|
||||
try
|
||||
{
|
||||
uiModule->ProcessChatBoxEntry(command);
|
||||
Log.Information("Submitted /sit after fishing started");
|
||||
}
|
||||
finally
|
||||
{
|
||||
command->Dtor(true);
|
||||
}
|
||||
satThisSession = true;
|
||||
sitAt = null;
|
||||
waitingForActionToFinish = false;
|
||||
}
|
||||
|
||||
private static bool IsFisher()
|
||||
{
|
||||
return PlayerState.IsLoaded && PlayerState.ClassJob.RowId == FisherClassJobId;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Condition.ConditionChange -= OnConditionChange;
|
||||
Framework.Update -= OnFrameworkUpdate;
|
||||
}
|
||||
}
|
||||
23
AutoFishingStool/README.md
Normal file
23
AutoFishingStool/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Auto Fishing Stool
|
||||
|
||||
Auto Fishing Stool is a small Dalamud plugin that uses `/sit` once after the player enters fishing mode. It waits for fishing to remain active and for any fishing action to finish before sitting.
|
||||
|
||||
## Build
|
||||
|
||||
From this directory, run:
|
||||
|
||||
```powershell
|
||||
dotnet build
|
||||
```
|
||||
|
||||
The development DLL is produced at `bin/Debug/AutoFishingStool.dll`.
|
||||
|
||||
## Load in Dalamud
|
||||
|
||||
1. Start Final Fantasy XIV through XIVLauncher with Dalamud enabled.
|
||||
2. Run `/xlsettings` and open the Experimental tab.
|
||||
3. Select `bin/Debug/AutoFishingStool.dll` under Dev Plugin Locations.
|
||||
4. Run `/xlplugins` and open Dev Tools, then Installed Dev Plugins.
|
||||
5. Enable Auto Fishing Stool.
|
||||
|
||||
The development plugin location only needs to be added once. Rebuild the plugin and reload it from the installed development plugins screen after future changes.
|
||||
BIN
AutoFishingStool/icon.png
Normal file
BIN
AutoFishingStool/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
19
AutoFishingStool/packages.lock.json
Normal file
19
AutoFishingStool/packages.lock.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net10.0-windows7.0": {
|
||||
"DalamudPackager": {
|
||||
"type": "Direct",
|
||||
"requested": "[15.0.0, )",
|
||||
"resolved": "15.0.0",
|
||||
"contentHash": "411vwC8/X8Z/sQ2TI6v3SvOn66xFPeOjFn3Zn+h0d3Ox2t1kFm66AhDvmx/qcMwVrR+Hidxj0dadpQ2dgyXMBQ=="
|
||||
},
|
||||
"DotNet.ReproducibleBuilds": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.2.39, )",
|
||||
"resolved": "1.2.39",
|
||||
"contentHash": "fcFN01tDTIQqDuTwr1jUQK/geofiwjG5DycJQOnC72i1SsLAk1ELe+apBOuZ11UMQG8YKFZG1FgvjZPbqHyatg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Bep
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Reference in New Issue
Block a user