Initial Commit

This commit is contained in:
Liza 2023-09-09 18:10:45 +02:00
commit c15f511f2b
Signed by: liza
GPG Key ID: 7199F8D727D55F67
10 changed files with 494 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.idea
*.user

16
ARDiscard.sln Normal file
View File

@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ARDiscard", "ARDiscard\ARDiscard.csproj", "{A9B4C542-1C83-4F46-81E7-5BAFCA109767}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A9B4C542-1C83-4F46-81E7-5BAFCA109767}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9B4C542-1C83-4F46-81E7-5BAFCA109767}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9B4C542-1C83-4F46-81E7-5BAFCA109767}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9B4C542-1C83-4F46-81E7-5BAFCA109767}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

3
ARDiscard/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/dist
/obj
/bin

View File

@ -0,0 +1,77 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<Version>1.0</Version>
<LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<OutputPath>dist</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>portable</DebugType>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup>
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
<DalamudLibPath>$(DALAMUD_HOME)/</DalamudLibPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.11"/>
</ItemGroup>
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="ImGui.NET">
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="ImGuiScene">
<HintPath>$(DalamudLibPath)ImGuiScene.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="AutoRetainerAPI">
<HintPath>$(appdata)\XIVLauncher\installedPlugins\AutoRetainer\4.1.0.6\AutoRetainerAPI.dll</HintPath>
</Reference>
<Reference Include="ECommons">
<HintPath>$(appdata)\XIVLauncher\installedPlugins\AutoRetainer\4.1.0.6\ECommons.dll</HintPath>
</Reference>
<Reference Include="ClickLib">
<HintPath>$(appdata)\XIVLauncher\installedPlugins\AutoRetainer\4.1.0.6\ClickLib.dll</HintPath>
</Reference>
</ItemGroup>
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin">
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/>
</Target>
</Project>

7
ARDiscard/ARDiscard.json Normal file
View File

@ -0,0 +1,7 @@
{
"Name": "Discard after AutoRetainer",
"Author": "Liza Carvelli",
"Punchline": "",
"Description": "",
"RepoUrl": "https://git.carvel.li/liza/ARDiscard"
}

View File

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using AutoRetainerAPI;
using ClickLib.Clicks;
using Dalamud.Data;
using Dalamud.Game.Command;
using Dalamud.Game.Gui;
using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Dalamud.Memory;
using Dalamud.Plugin;
using ECommons;
using ECommons.Automation;
using ECommons.DalamudServices;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace ARDiscard;
public class AutoDiscardPlogon : IDalamudPlugin
{
private readonly WindowSystem _windowSystem = new(nameof(AutoDiscardPlogon));
private readonly Configuration _configuration;
private readonly ConfigWindow _configWindow;
private readonly DalamudPluginInterface _pluginInterface;
private readonly ChatGui _chatGui;
private readonly CommandManager _commandManager;
private readonly InventoryUtils _inventoryUtils;
private readonly AutoRetainerApi _autoRetainerApi;
private readonly TaskManager _taskManager;
public AutoDiscardPlogon(DalamudPluginInterface pluginInterface, CommandManager commandManager, ChatGui chatGui,
DataManager dataManager)
{
_pluginInterface = pluginInterface;
_configuration = (Configuration?)_pluginInterface.GetPluginConfig() ?? new Configuration();
_chatGui = chatGui;
_commandManager = commandManager;
_commandManager.AddHandler("/discardconfig", new CommandInfo(OpenConfig));
_commandManager.AddHandler("/discardall", new CommandInfo(ProcessCommand));
_inventoryUtils = new(_configuration);
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
_pluginInterface.UiBuilder.OpenConfigUi += OpenConfigUi;
_configWindow = new(_pluginInterface, _configuration, dataManager);
_configWindow.IsOpen = true;
_windowSystem.AddWindow(_configWindow);
ECommonsMain.Init(_pluginInterface, this);
_autoRetainerApi = new();
_taskManager = new();
_autoRetainerApi.OnRetainerReadyToPostprocess += DoPostProcess;
_autoRetainerApi.OnRetainerPostprocessStep += CheckPostProcess;
}
public string Name => "Discard after AutoRetainer";
private unsafe void CheckPostProcess(string retainerName)
{
if (_inventoryUtils.GetNextItemToDiscard() != null)
_autoRetainerApi.RequestPostprocess();
}
private void DoPostProcess(string retainerName)
{
_taskManager.Enqueue(() => DiscardNextItem(true));
}
private void OpenConfig(string command, string arguments) => OpenConfigUi();
private void OpenConfigUi()
{
_configWindow.IsOpen = true;
}
private void ProcessCommand(string command, string arguments)
{
_taskManager.Enqueue(() => DiscardNextItem(false));
}
private unsafe void DiscardNextItem(bool finishRetainerAction)
{
InventoryItem* nextItem = _inventoryUtils.GetNextItemToDiscard();
if (nextItem == null)
{
if (finishRetainerAction)
_autoRetainerApi.FinishPostProcess();
else
_chatGui.Print("Done discarding.");
return;
}
var (inventoryType, slot) = (nextItem->Container, nextItem->Slot);
//_chatGui.Print($"Discarding {nextItem->ItemID}, {nextItem->Container}, {nextItem->Slot}.");
_inventoryUtils.Discard(nextItem);
_taskManager.DelayNext(5);
_taskManager.Enqueue(ConfirmDiscardItem);
_taskManager.DelayNext(2000);
_taskManager.Enqueue(() => ContinueAfterDiscard(finishRetainerAction, inventoryType, slot));
}
private unsafe void ConfirmDiscardItem()
{
var addon = GetDiscardAddon();
if (addon != null)
{
((AddonSelectYesno*)addon)->YesButton->AtkComponentBase.SetEnabledState(true);
ClickSelectYesNo.Using((nint)addon).Yes();
}
}
private unsafe void ContinueAfterDiscard(bool finishRetainerAction, InventoryType inventoryType, short slot)
{
InventoryItem* nextItem = _inventoryUtils.GetNextItemToDiscard();
if (nextItem == null)
{
if (finishRetainerAction)
_autoRetainerApi.FinishPostProcess();
else
_chatGui.Print("Done discarding.");
return;
}
if (nextItem->Container == inventoryType && nextItem->Slot == slot)
{
_taskManager.DelayNext(100);
_taskManager.Enqueue(() => ContinueAfterDiscard(finishRetainerAction, inventoryType, slot));
}
else
{
_taskManager.EnqueueImmediate(() => DiscardNextItem(finishRetainerAction));
}
}
public void Dispose()
{
_autoRetainerApi.Dispose();
ECommonsMain.Dispose();
_inventoryUtils.Dispose();
_pluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUi;
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
_commandManager.RemoveHandler("/discardall");
_commandManager.RemoveHandler("/discardconfig");
}
private static unsafe AtkUnitBase* GetDiscardAddon()
{
for (int i = 1; i < 100; i++)
{
try
{
var addon = (AtkUnitBase*)Svc.GameGui.GetAddonByName("SelectYesno", i);
if (addon == null) return null;
if (addon->IsVisible)
{
var textNode = addon->UldManager.NodeList[15]->GetAsAtkTextNode();
var text = MemoryHelper.ReadSeString(&textNode->NodeText).ExtractText();
PluginLog.Information($"TEt → {text}");
if (text.StartsWith("Discard"))
{
return addon;
}
}
}
catch (Exception)
{
return null;
}
}
return null;
}
}

125
ARDiscard/ConfigWindow.cs Normal file
View File

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Data;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using ECommons;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
namespace ARDiscard;
public class ConfigWindow : Window
{
private readonly DalamudPluginInterface _pluginInterface;
private readonly Configuration _configuration;
private readonly DataManager _dataManager;
private string _itemName = string.Empty;
private List<(uint ItemId, string Name)> _searchResults = new();
private List<(uint ItemId, string Name)> _discarding = new();
public ConfigWindow(DalamudPluginInterface pluginInterface, Configuration configuration, DataManager dataManager)
: base("Auto Discard###AutoDiscardConfig")
{
_pluginInterface = pluginInterface;
_configuration = configuration;
_dataManager = dataManager;
Size = new Vector2(600, 400);
SizeCondition = ImGuiCond.FirstUseEver;
SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(600, 400),
MaximumSize = new Vector2(9999, 9999),
};
_discarding = _configuration.DiscardingItems
.Select(x => (x, dataManager.GetExcelSheet<Item>()?.GetRow(x)?.Name?.ToString() ?? x.ToString())).ToList();
}
public override void Draw()
{
var ws = ImGui.GetWindowSize();
if (ImGui.BeginChild("Left", new Vector2(Math.Max(10, ws.X / 2), -1), true))
{
ImGui.Text("Search");
ImGui.SetNextItemWidth(ws.X / 2 - 20);
if (ImGui.InputText("", ref _itemName, 256))
UpdateResults();
ImGui.Separator();
if (_searchResults.Count == 0)
{
ImGui.Text("Type item name...");
}
foreach (var (id, name) in _searchResults)
{
bool selected = _discarding.Any(x => x.Item1 == id);
if (ImGui.Selectable(name, selected))
{
if (!selected)
{
_discarding.Add((id, name));
}
else
{
_discarding.Remove((id, name));
}
_configuration.DiscardingItems = _discarding.Select(x => x.ItemId).ToList();
_pluginInterface.SavePluginConfig(_configuration);
}
}
}
ImGui.EndChild();
ImGui.SameLine();
if (ImGui.BeginChild("Right", new Vector2(-1, -1), true, ImGuiWindowFlags.NoSavedSettings))
{
ImGui.Text("Items that will be automatically discarded");
ImGui.Separator();
List<(uint, string)> toRemove = new();
foreach (var (id, name) in _discarding.OrderBy(x => x.Name.ToLower()))
{
if (ImGui.Selectable(name, true))
toRemove.Add((id, name));
}
if (toRemove.Count > 0)
{
foreach (var tr in toRemove)
_discarding.Remove(tr);
_configuration.DiscardingItems = _discarding.Select(x => x.ItemId).ToList();
_pluginInterface.SavePluginConfig(_configuration);
}
}
ImGui.EndChild();
}
private void UpdateResults()
{
if (string.IsNullOrEmpty(_itemName))
_searchResults = new();
else
{
_searchResults = _dataManager.GetExcelSheet<Item>()
.Where(x => x.RowId != 0)
.Where(x => !x.IsUnique && !x.IsUntradable)
.Where(x => x.ItemUICategory?.Value?.Name?.ToString() != "Currency")
.Where(x => !string.IsNullOrEmpty(x.Name) &&
x.Name.ToString().Contains(_itemName, StringComparison.CurrentCultureIgnoreCase))
.Select(x => (x.RowId, x.Name.ToString()))
.ToList();
}
}
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
using Dalamud.Configuration;
namespace ARDiscard;
public class Configuration : IPluginConfiguration
{
public int Version { get; set; } = 1;
public List<uint> DiscardingItems = new();
}

View File

@ -0,0 +1,59 @@
using System;
using Dalamud.Hooking;
using Dalamud.Logging;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
namespace ARDiscard;
public class InventoryUtils : IDisposable
{
private readonly Configuration _configuration;
private static readonly InventoryType[] InventoryTypes =
{ InventoryType.Inventory1, InventoryType.Inventory2, InventoryType.Inventory3, InventoryType.Inventory4 };
private unsafe delegate void DiscardItemDelegate(AgentInventoryContext* inventoryManager, InventoryItem* itemSlot,
InventoryType inventory, int slot, uint addonId, int position = -1);
[Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 74 ?? 0F B7 48")]
private DiscardItemDelegate _discardItem = null!;
public InventoryUtils(Configuration configuration)
{
_configuration = configuration;
SignatureHelper.Initialise(this);
}
public void Dispose()
{
}
public unsafe InventoryItem* GetNextItemToDiscard()
{
InventoryManager* inventoryManager = InventoryManager.Instance();
foreach (InventoryType inventoryType in InventoryTypes)
{
InventoryContainer* container = inventoryManager->GetInventoryContainer(inventoryType);
for (int i = 0; i < container->Size; ++i)
{
var item = container->GetInventorySlot(i);
if (item != null)
{
if (_configuration.DiscardingItems.Contains(item->ItemID))
{
return item;
}
}
}
}
return null;
}
public unsafe void Discard(InventoryItem* item)
{
_discardItem(AgentInventoryContext.Instance(), item, item->Container, item->Slot, 0);
}
}

View File

@ -0,0 +1,13 @@
{
"version": 1,
"dependencies": {
"net7.0-windows7.0": {
"DalamudPackager": {
"type": "Direct",
"requested": "[2.1.11, )",
"resolved": "2.1.11",
"contentHash": "9qlAWoRRTiL/geAvuwR/g6Bcbrd/bJJgVnB/RurBiyKs6srsP0bvpoo8IK+Eg8EA6jWeM6/YJWs66w4FIAzqPw=="
}
}
}
}