Initial project
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
bin/
|
bin/
|
||||||
obj/
|
obj/
|
||||||
|
.idea/
|
||||||
/packages/
|
/packages/
|
||||||
riderModule.iml
|
riderModule.iml
|
||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "KamiToolKit"]
|
||||||
|
path = KamiToolKit
|
||||||
|
url = https://github.com/MidoriKami/KamiToolKit
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AetherBags", "AetherBags\AetherBags.csproj", "{5BBE4215-8189-4A8A-AFD0-C5C6074DB47E}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AetherBags", "AetherBags\AetherBags.csproj", "{5BBE4215-8189-4A8A-AFD0-C5C6074DB47E}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KamiToolKit", "KamiToolKit\KamiToolKit.csproj", "{0907374F-93F8-427F-AD0A-49DB4B0A3DD4}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
@@ -12,5 +14,9 @@ Global
|
|||||||
{5BBE4215-8189-4A8A-AFD0-C5C6074DB47E}.Debug|x64.Build.0 = Debug|x64
|
{5BBE4215-8189-4A8A-AFD0-C5C6074DB47E}.Debug|x64.Build.0 = Debug|x64
|
||||||
{5BBE4215-8189-4A8A-AFD0-C5C6074DB47E}.Release|x64.ActiveCfg = Release|x64
|
{5BBE4215-8189-4A8A-AFD0-C5C6074DB47E}.Release|x64.ActiveCfg = Release|x64
|
||||||
{5BBE4215-8189-4A8A-AFD0-C5C6074DB47E}.Release|x64.Build.0 = Release|x64
|
{5BBE4215-8189-4A8A-AFD0-C5C6074DB47E}.Release|x64.Build.0 = Release|x64
|
||||||
|
{0907374F-93F8-427F-AD0A-49DB4B0A3DD4}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{0907374F-93F8-427F-AD0A-49DB4B0A3DD4}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{0907374F-93F8-427F-AD0A-49DB4B0A3DD4}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{0907374F-93F8-427F-AD0A-49DB4B0A3DD4}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAgentSatisfactionSupply_002Ecs_002Fl_003AC_0021_003FUsers_003FJeffro_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb2cd0663609440e590f52980cafc1ba3822648_003F28_003Ffa48b62e_003FAgentSatisfactionSupply_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/.idea/
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using AetherBags.Extensions;
|
||||||
|
using AetherBags.Inventory;
|
||||||
|
using AetherBags.Nodes;
|
||||||
|
using Dalamud.Game.Addon.Lifecycle;
|
||||||
|
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
using KamiToolKit;
|
||||||
|
using KamiToolKit.Classes;
|
||||||
|
|
||||||
|
namespace AetherBags.Addons;
|
||||||
|
|
||||||
|
public class AddonInventoryWindow : NativeAddon
|
||||||
|
{
|
||||||
|
private InventoryCategoryNode _categoryNode;
|
||||||
|
private InventoryDragDropNode _dragDropNode;
|
||||||
|
protected override unsafe void OnSetup(AtkUnitBase* addon)
|
||||||
|
{
|
||||||
|
Services.AddonLifecycle.RegisterListener(AddonEvent.PostRequestedUpdate, "Inventory", OnInventoryUpdate);
|
||||||
|
_categoryNode = new InventoryCategoryNode
|
||||||
|
{
|
||||||
|
Position = ContentStartPosition,
|
||||||
|
Size = ContentSize,
|
||||||
|
Category = new CategoryInfo
|
||||||
|
{
|
||||||
|
Name = "AetherBags",
|
||||||
|
},
|
||||||
|
Items = InventoryState.GetInventoryItems()
|
||||||
|
};
|
||||||
|
_categoryNode.AttachNode(this);
|
||||||
|
/*
|
||||||
|
var data = InventoryState.GetInventoryItems().Find(item => item.Name.Contains("Cookie"));
|
||||||
|
|
||||||
|
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
|
var item = data.Item;
|
||||||
|
_dragDropNode = new InventoryDragDropNode
|
||||||
|
{
|
||||||
|
Size = new Vector2(48),
|
||||||
|
IsVisible = true,
|
||||||
|
IconId = data.IconId,
|
||||||
|
AcceptedType = DragDropType.Nothing,
|
||||||
|
IsDraggable = false,
|
||||||
|
Payload = new DragDropPayload
|
||||||
|
{
|
||||||
|
Type = DragDropType.Item,
|
||||||
|
Int1 = (int)data.Item.Container,
|
||||||
|
Int2 = (int)data.Item.ItemId,
|
||||||
|
},
|
||||||
|
IsClickable = true,
|
||||||
|
OnRollOver = node => node.ShowInventoryItemTooltip(data.Item.Container, data.Item.Slot),
|
||||||
|
OnRollOut = node => node.HideTooltip(),
|
||||||
|
OnClicked = _ =>
|
||||||
|
{
|
||||||
|
|
||||||
|
AgentInventoryContext* context = AgentInventoryContext.Instance();
|
||||||
|
context->OpenForItemSlot(data.Item.Container, data.Item.Slot, 0, context->AddonId);
|
||||||
|
//item.UseItem();
|
||||||
|
},
|
||||||
|
ItemInfo = data
|
||||||
|
};
|
||||||
|
_dragDropNode.AttachNode(this);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override unsafe void OnUpdate(AtkUnitBase* addon)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInventoryUpdate(AddonEvent type, AddonArgs args)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override unsafe void OnFinalize(AtkUnitBase* addon)
|
||||||
|
{
|
||||||
|
Services.AddonLifecycle.UnregisterListener(OnInventoryUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,24 @@
|
|||||||
<Project Sdk="Dalamud.NET.Sdk/12.0.2">
|
<Project Sdk="Dalamud.NET.Sdk/14.0.1">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>1.0.0.0</Version>
|
<Version>1.0.0.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Author>Zeffuro</Author>
|
||||||
|
<Name>AetherBags</Name>
|
||||||
|
<InternalName>AetherBags</InternalName>
|
||||||
|
<Punchline>Never think too hard about your bags again!</Punchline>
|
||||||
|
<Description>This plugin replaces your inventory with it's own categorified inventory addon.</Description>
|
||||||
|
<RepoUrl>https://github.com/Zeffuro/AetherBags</RepoUrl>
|
||||||
|
<Tags>ui</Tags>
|
||||||
|
<AcceptsFeedback>true</AcceptsFeedback>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\KamiToolKit\KamiToolKit.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include=".gitignore" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"Name": "AetherBags",
|
|
||||||
"Author": "",
|
|
||||||
"Description": "",
|
|
||||||
"Punchline": ""
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
|
namespace AetherBags.Extensions;
|
||||||
|
|
||||||
|
public static unsafe class AtkResNodeExtensions
|
||||||
|
{
|
||||||
|
extension(ref AtkResNode node)
|
||||||
|
{
|
||||||
|
public void ShowInventoryItemTooltip(InventoryType container, short slot) {
|
||||||
|
fixed (AtkResNode* nodePointer = &node) {
|
||||||
|
AtkStage.Instance()->ShowInventoryItemTooltip(nodePointer, container, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using FFXIVClientStructs.FFXIV.Client.Enums;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
|
namespace AetherBags.Extensions;
|
||||||
|
|
||||||
|
|
||||||
|
public static unsafe class AtkStageExtensions
|
||||||
|
{
|
||||||
|
extension(ref AtkStage stage)
|
||||||
|
{
|
||||||
|
public void ShowInventoryItemTooltip(AtkResNode* node, InventoryType container, short slot)
|
||||||
|
{
|
||||||
|
var tooltipArgs = stackalloc AtkTooltipManager.AtkTooltipArgs[1];
|
||||||
|
tooltipArgs->Ctor();
|
||||||
|
tooltipArgs->ItemArgs.Kind = DetailKind.InventoryItem;
|
||||||
|
tooltipArgs->ItemArgs.InventoryType = container;
|
||||||
|
tooltipArgs->ItemArgs.Slot = slot;
|
||||||
|
tooltipArgs->ItemArgs.BuyQuantity = -1;
|
||||||
|
tooltipArgs->ItemArgs.Flag1 = 0;
|
||||||
|
|
||||||
|
var addon = RaptureAtkUnitManager.Instance()->GetAddonByNode(node);
|
||||||
|
if (addon is null) return;
|
||||||
|
|
||||||
|
stage.TooltipManager.ShowTooltip(
|
||||||
|
AtkTooltipManager.AtkTooltipType.Item,
|
||||||
|
addon->Id,
|
||||||
|
node,
|
||||||
|
tooltipArgs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
using Lumina.Text.ReadOnly;
|
||||||
|
|
||||||
|
namespace AetherBags.Extensions;
|
||||||
|
|
||||||
|
public static unsafe class InventoryItemExtensions {
|
||||||
|
extension(ref InventoryItem item) {
|
||||||
|
public uint IconId => item.GetIconId();
|
||||||
|
public ReadOnlySeString Name => item.GetItemName();
|
||||||
|
|
||||||
|
private uint GetIconId() {
|
||||||
|
uint iconId = 0;
|
||||||
|
|
||||||
|
if (item.GetEventItem() is { } eventItem) {
|
||||||
|
iconId = eventItem.Icon;
|
||||||
|
}
|
||||||
|
else if (item.GetItem() is { } regularItem) {
|
||||||
|
iconId = regularItem.Icon;
|
||||||
|
|
||||||
|
if (item.IsHighQuality()) {
|
||||||
|
iconId += 1_000_000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iconId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReadOnlySeString GetItemName() {
|
||||||
|
var itemId = item.GetItemId();
|
||||||
|
var itemName = ItemUtil.GetItemName(itemId);
|
||||||
|
|
||||||
|
return new Lumina.Text.SeStringBuilder()
|
||||||
|
.PushColorType(ItemUtil.GetItemRarityColorType(itemId))
|
||||||
|
.Append(itemName)
|
||||||
|
.PopColorType()
|
||||||
|
.ToReadOnlySeString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Item? GetItem() {
|
||||||
|
var baseItemId = item.GetBaseItemId();
|
||||||
|
|
||||||
|
if (ItemUtil.IsNormalItem(baseItemId) &&
|
||||||
|
Services.DataManager.GetExcelSheet<Item>().TryGetRow(baseItemId, out var baseItem)) {
|
||||||
|
return baseItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventItem? GetEventItem() {
|
||||||
|
var baseItemId = item.GetBaseItemId();
|
||||||
|
|
||||||
|
if (ItemUtil.IsEventItem(baseItemId) &&
|
||||||
|
Services.DataManager.GetExcelSheet<EventItem>().TryGetRow(baseItemId, out var eventItem)) {
|
||||||
|
return eventItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsRegexMatch(string searchString) {
|
||||||
|
// Skip any data access if string is empty
|
||||||
|
if (searchString.IsNullOrEmpty()) return true;
|
||||||
|
|
||||||
|
var isDescriptionSearch = searchString.StartsWith('$');
|
||||||
|
|
||||||
|
if (isDescriptionSearch) {
|
||||||
|
searchString = searchString[1..];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var regex = new Regex(searchString,RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
if (ItemUtil.IsEventItem(item.GetBaseItemId())) {
|
||||||
|
if (!Services.DataManager.GetExcelSheet<EventItem>().TryGetRow(item.GetBaseItemId(), out var itemData)) return false;
|
||||||
|
|
||||||
|
if (regex.IsMatch(item.ItemId.ToString())) return true;
|
||||||
|
if (regex.IsMatch(itemData.Name.ToString())) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (ItemUtil.IsNormalItem(item.GetBaseItemId())) {
|
||||||
|
if (!Services.DataManager.GetExcelSheet<Item>().TryGetRow(item.GetBaseItemId(), out var itemData)) return false;
|
||||||
|
|
||||||
|
if (regex.IsMatch(item.ItemId.ToString())) return true;
|
||||||
|
if (regex.IsMatch(itemData.Name.ToString())) return true;
|
||||||
|
if (regex.IsMatch(itemData.Description.ToString()) && isDescriptionSearch) return true;
|
||||||
|
if (regex.IsMatch(itemData.LevelEquip.ToString())) return true;
|
||||||
|
if (regex.IsMatch(itemData.LevelItem.RowId.ToString())) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (RegexParseException) { }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UseItem()
|
||||||
|
{
|
||||||
|
uint itemId = item.ItemId;
|
||||||
|
InventoryType type = item.GetInventoryType() == InventoryType.KeyItems
|
||||||
|
? InventoryType.KeyItems
|
||||||
|
: InventoryType.Invalid;
|
||||||
|
|
||||||
|
if (InventoryManager.Instance()->GetInventoryItemCount(itemId, true) > 0)
|
||||||
|
itemId += 1_000_000;
|
||||||
|
|
||||||
|
AgentInventoryContext.Instance()->UseItem(itemId, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using KamiToolKit.Classes;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
|
||||||
|
namespace AetherBags.Extensions;
|
||||||
|
|
||||||
|
public static class ItemExtensions {
|
||||||
|
extension(Item item) {
|
||||||
|
public Vector4 RarityColor => item.Rarity switch {
|
||||||
|
7 => ColorHelper.GetColor(561),
|
||||||
|
4 => ColorHelper.GetColor(555),
|
||||||
|
3 => ColorHelper.GetColor(553),
|
||||||
|
2 => ColorHelper.GetColor(551),
|
||||||
|
1 => ColorHelper.GetColor(549),
|
||||||
|
_ => Vector4.One,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
using KamiToolKit;
|
||||||
|
|
||||||
|
namespace AetherBags.Extensions;
|
||||||
|
|
||||||
|
public static unsafe class NodeBaseExtensions {
|
||||||
|
extension(NodeBase node) {
|
||||||
|
public void ShowInventoryItemTooltip(InventoryType container, short slot)
|
||||||
|
=> AtkStage.Instance()->ShowInventoryItemTooltip(node, container, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
global using KamiToolKit.Extensions;
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
|
||||||
|
namespace AetherBags.Helpers;
|
||||||
|
|
||||||
|
// Taken and adapted for StatusTimers using zips from https://github.com/Caraxi/SimpleHeels/blob/0a0fe3c02a0a2c5a7c96b3304952d5078cd338aa/Plugin.cs#L392
|
||||||
|
// Thanks Caraxi
|
||||||
|
public static class BackupHelper {
|
||||||
|
private const int MaxBackups = 10;
|
||||||
|
private const string Name = "StatusTimers";
|
||||||
|
public static void DoConfigBackup(IDalamudPluginInterface pluginInterface) {
|
||||||
|
Services.Logger.Debug("Backup configuration start.");
|
||||||
|
try {
|
||||||
|
var configDirectory = pluginInterface.ConfigDirectory;
|
||||||
|
if (!configDirectory.Exists) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var backupDir = Path.Join(configDirectory.Parent!.Parent!.FullName, "backups", Name);
|
||||||
|
var dir = new DirectoryInfo(backupDir);
|
||||||
|
if (!dir.Exists) {
|
||||||
|
dir.Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dir.Exists) {
|
||||||
|
throw new Exception("Backup Directory does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
var latestFile = new FileInfo(Path.Join(backupDir, $"{Name}.latest.zip"));
|
||||||
|
var tempFile = Path.Join(backupDir, $"{Name}.tmp.zip");
|
||||||
|
|
||||||
|
var needsBackup = false;
|
||||||
|
|
||||||
|
if (latestFile.Exists) {
|
||||||
|
string lastBackupHash = ZipJsonHash(latestFile.FullName);
|
||||||
|
string currentConfigDirHash = DirJsonHash(configDirectory.FullName);
|
||||||
|
if (currentConfigDirHash != lastBackupHash) {
|
||||||
|
needsBackup = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
needsBackup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needsBackup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipFile.CreateFromDirectory(configDirectory.FullName, tempFile);
|
||||||
|
if (latestFile.Exists) {
|
||||||
|
var t = latestFile.LastWriteTime;
|
||||||
|
string archiveName = $"{Name}.{t.Year}{t.Month:00}{t.Day:00}{t.Hour:00}{t.Minute:00}{t.Second:00}.zip";
|
||||||
|
string archivePath = Path.Join(backupDir, archiveName);
|
||||||
|
|
||||||
|
bool moved = false;
|
||||||
|
for (int i = 0; i < 5 && !moved; i++) {
|
||||||
|
try {
|
||||||
|
File.Move(latestFile.FullName, archivePath);
|
||||||
|
moved = true;
|
||||||
|
} catch (IOException ioEx) when (i < 4) {
|
||||||
|
Services.Logger.Debug($"Move failed, retrying in 100ms: {ioEx.Message}");
|
||||||
|
global::System.Threading.Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!moved) {
|
||||||
|
throw new IOException($"Could not move {latestFile.FullName} after several retries.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(latestFile.FullName)) {
|
||||||
|
File.Delete(latestFile.FullName);
|
||||||
|
}
|
||||||
|
File.Move(tempFile, latestFile.FullName);
|
||||||
|
|
||||||
|
var allBackups = dir.GetFiles().Where(f => f.Name.StartsWith($"{Name}.2") && f.Name.EndsWith(".zip"))
|
||||||
|
.OrderBy(f => f.LastWriteTime.Ticks).ToList();
|
||||||
|
if (allBackups.Count > MaxBackups) {
|
||||||
|
Services.Logger.Debug($"Removing Oldest Backup: {allBackups[0].FullName}");
|
||||||
|
File.Delete(allBackups[0].FullName);
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Services.Logger.Warning(exception, "Backup Skipped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ComputeCombinedJsonHash(IEnumerable<(string name, byte[] contents)> files) {
|
||||||
|
using var sha256 = SHA256.Create();
|
||||||
|
foreach (var file in files.OrderBy(f => f.name, StringComparer.OrdinalIgnoreCase)) {
|
||||||
|
sha256.TransformBlock(file.contents, 0, file.contents.Length, null, 0);
|
||||||
|
}
|
||||||
|
sha256.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
|
||||||
|
return sha256.Hash != null ? BitConverter.ToString(sha256.Hash).Replace("-", "") : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DirJsonHash(string dirPath) =>
|
||||||
|
ComputeCombinedJsonHash(
|
||||||
|
new DirectoryInfo(dirPath)
|
||||||
|
.GetFiles("*.json", SearchOption.TopDirectoryOnly)
|
||||||
|
.Where(f => !f.Name.EndsWith(".addon.json", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.Select(f => (f.Name, File.ReadAllBytes(f.FullName)))
|
||||||
|
);
|
||||||
|
|
||||||
|
private static string ZipJsonHash(string zipPath) {
|
||||||
|
byte[] zipBytes = File.ReadAllBytes(zipPath);
|
||||||
|
using var msZip = new MemoryStream(zipBytes);
|
||||||
|
using var zip = new ZipArchive(msZip, ZipArchiveMode.Read);
|
||||||
|
var files = zip.Entries
|
||||||
|
.Where(e => e.FullName.EndsWith(".json", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !e.FullName.EndsWith(".addon.json", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !e.FullName.Contains("/"))
|
||||||
|
.Select(e => {
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
using (var s = e.Open()) {
|
||||||
|
s.CopyTo(ms);
|
||||||
|
}
|
||||||
|
return (e.FullName, ms.ToArray());
|
||||||
|
});
|
||||||
|
return ComputeCombinedJsonHash(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using KamiToolKit.Classes;
|
||||||
|
|
||||||
|
namespace AetherBags.Inventory;
|
||||||
|
|
||||||
|
public class CategoryInfo
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public Vector4 Color { get; set; } = ColorHelper.GetColor(50);
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Game.Inventory;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
|
||||||
|
namespace AetherBags.Inventory;
|
||||||
|
|
||||||
|
public static unsafe class InventoryState
|
||||||
|
{
|
||||||
|
public static List<InventoryType> StandardInventories => [
|
||||||
|
InventoryType.Inventory1,
|
||||||
|
InventoryType.Inventory2,
|
||||||
|
InventoryType.Inventory3,
|
||||||
|
InventoryType.Inventory4,
|
||||||
|
InventoryType.EquippedItems,
|
||||||
|
InventoryType.ArmoryMainHand,
|
||||||
|
InventoryType.ArmoryHead,
|
||||||
|
InventoryType.ArmoryBody,
|
||||||
|
InventoryType.ArmoryHands,
|
||||||
|
InventoryType.ArmoryWaist,
|
||||||
|
InventoryType.ArmoryLegs,
|
||||||
|
InventoryType.ArmoryFeets,
|
||||||
|
InventoryType.ArmoryOffHand,
|
||||||
|
InventoryType.ArmoryEar,
|
||||||
|
InventoryType.ArmoryNeck,
|
||||||
|
InventoryType.ArmoryWrist,
|
||||||
|
InventoryType.ArmoryRings,
|
||||||
|
InventoryType.Currency,
|
||||||
|
InventoryType.Crystals,
|
||||||
|
InventoryType.ArmorySoulCrystal,
|
||||||
|
];
|
||||||
|
|
||||||
|
public static bool Contains(this List<InventoryType> inventoryTypes, GameInventoryType type)
|
||||||
|
=> inventoryTypes.Contains((InventoryType)type);
|
||||||
|
|
||||||
|
public static List<ItemInfo> GetInventoryItems() {
|
||||||
|
List<InventoryType> inventories = [ InventoryType.Inventory1, InventoryType.Inventory2, InventoryType.Inventory3, InventoryType.Inventory4 ];
|
||||||
|
List<InventoryItem> items = [];
|
||||||
|
|
||||||
|
foreach (var inventory in inventories) {
|
||||||
|
var container = InventoryManager.Instance()->GetInventoryContainer(inventory);
|
||||||
|
|
||||||
|
for (var index = 0; index < container->Size; ++index) {
|
||||||
|
ref var item = ref container->Items[index];
|
||||||
|
if (item.ItemId is 0) continue;
|
||||||
|
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ItemInfo> itemInfos = [];
|
||||||
|
itemInfos.AddRange(from itemGroups in items.GroupBy(item => item.ItemId)
|
||||||
|
where itemGroups.Key is not 0
|
||||||
|
let item = itemGroups.First()
|
||||||
|
let itemCount = itemGroups.Sum(duplicateItem => duplicateItem.Quantity)
|
||||||
|
select new ItemInfo {
|
||||||
|
Item = item, ItemCount = itemCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
return itemInfos;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using AetherBags.Extensions;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
|
||||||
|
namespace AetherBags.Inventory;
|
||||||
|
|
||||||
|
public class ItemInfo : IEquatable<ItemInfo> {
|
||||||
|
public required InventoryItem Item { get; set; }
|
||||||
|
public required int ItemCount { get; set; }
|
||||||
|
|
||||||
|
private Item ItemData => Services.DataManager.GetExcelSheet<Item>().GetRow(Item.ItemId);
|
||||||
|
|
||||||
|
public Vector4 RarityColor => ItemData.RarityColor;
|
||||||
|
|
||||||
|
public uint IconId => ItemData.Icon;
|
||||||
|
|
||||||
|
public string Name => ItemData.Name.ToString();
|
||||||
|
|
||||||
|
public int Level => ItemData.LevelEquip;
|
||||||
|
|
||||||
|
public int ItemLevel => (int) ItemData.LevelItem.RowId;
|
||||||
|
|
||||||
|
public int Rarity => ItemData.Rarity;
|
||||||
|
|
||||||
|
public int UiCategory => (int) ItemData.ItemUICategory.RowId;
|
||||||
|
|
||||||
|
private string Description => ItemData.Description.ToString();
|
||||||
|
|
||||||
|
public bool IsRegexMatch(string searchTerms) {
|
||||||
|
const RegexOptions regexOptions = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase;
|
||||||
|
|
||||||
|
if (Regex.IsMatch(Name, searchTerms, regexOptions)) return true;
|
||||||
|
if (Regex.IsMatch(Description, searchTerms, regexOptions)) return true;
|
||||||
|
if (Regex.IsMatch(Level.ToString(), searchTerms, regexOptions)) return true;
|
||||||
|
if (Regex.IsMatch(ItemLevel.ToString(), searchTerms, regexOptions)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ItemInfo? other) {
|
||||||
|
if (other is null) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return Item.ItemId.Equals(other.Item.ItemId) && ItemCount == other.ItemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) {
|
||||||
|
if (obj is null) return false;
|
||||||
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
|
if (obj.GetType() != GetType()) return false;
|
||||||
|
return Equals((ItemInfo) obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
// ReSharper disable NonReadonlyMemberInGetHashCode
|
||||||
|
=> HashCode.Combine(Item.ItemId, ItemCount);
|
||||||
|
// ReSharper restore NonReadonlyMemberInGetHashCode
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using KamiToolKit;
|
||||||
|
using KamiToolKit.Nodes;
|
||||||
|
|
||||||
|
namespace AetherBags.Nodes
|
||||||
|
{
|
||||||
|
public enum FlexGrowDirection
|
||||||
|
{
|
||||||
|
DownRight,
|
||||||
|
DownLeft,
|
||||||
|
UpRight,
|
||||||
|
UpLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HybridDirectionalFlexNode : HybridDirectionalFlexNode<NodeBase> { }
|
||||||
|
|
||||||
|
public class HybridDirectionalFlexNode<T> : LayoutListNode where T : NodeBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public FlexGrowDirection GrowDirection { get;
|
||||||
|
set {
|
||||||
|
field = value;
|
||||||
|
RecalculateLayout();
|
||||||
|
}
|
||||||
|
} = FlexGrowDirection.DownRight;
|
||||||
|
|
||||||
|
public int ItemsPerLine {
|
||||||
|
get;
|
||||||
|
set {
|
||||||
|
field = value;
|
||||||
|
RecalculateLayout();
|
||||||
|
}
|
||||||
|
} = 1;
|
||||||
|
|
||||||
|
public bool FillRowsFirst {
|
||||||
|
get;
|
||||||
|
set {
|
||||||
|
field = value;
|
||||||
|
RecalculateLayout();
|
||||||
|
}
|
||||||
|
} = true;
|
||||||
|
|
||||||
|
public float HorizontalPadding {
|
||||||
|
get;
|
||||||
|
set {
|
||||||
|
field = value;
|
||||||
|
RecalculateLayout();
|
||||||
|
}
|
||||||
|
} = 1;
|
||||||
|
|
||||||
|
public float VerticalPadding {
|
||||||
|
get;
|
||||||
|
set {
|
||||||
|
field = value;
|
||||||
|
RecalculateLayout();
|
||||||
|
}
|
||||||
|
} = 1;
|
||||||
|
|
||||||
|
protected override void InternalRecalculateLayout()
|
||||||
|
{
|
||||||
|
if (NodeList.Count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int itemsPerLine = Math.Max(1, ItemsPerLine);
|
||||||
|
|
||||||
|
float nodeWidth = NodeList.First().Width;
|
||||||
|
float nodeHeight = NodeList.First().Height;
|
||||||
|
|
||||||
|
bool alignRight = GrowDirection is FlexGrowDirection.DownLeft or FlexGrowDirection.UpLeft;
|
||||||
|
bool alignBottom = GrowDirection is FlexGrowDirection.UpRight or FlexGrowDirection.UpLeft;
|
||||||
|
|
||||||
|
float startX = alignRight ? Width : 0f;
|
||||||
|
float startY = alignBottom ? Height : 0f;
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
foreach (var node in NodeList)
|
||||||
|
{
|
||||||
|
int row, col;
|
||||||
|
if (FillRowsFirst)
|
||||||
|
{
|
||||||
|
row = idx / itemsPerLine;
|
||||||
|
col = idx % itemsPerLine;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
col = idx / itemsPerLine;
|
||||||
|
row = idx % itemsPerLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
float x = alignRight
|
||||||
|
? startX - (col + 1) * nodeWidth - col * HorizontalPadding
|
||||||
|
: startX + col * (nodeWidth + HorizontalPadding);
|
||||||
|
|
||||||
|
float y = alignBottom
|
||||||
|
? startY - (row + 1) * nodeHeight - row * VerticalPadding
|
||||||
|
: startY + row * (nodeHeight + VerticalPadding);
|
||||||
|
|
||||||
|
node.X = x;
|
||||||
|
node.Y = y;
|
||||||
|
AdjustNode(node);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using AetherBags.Extensions;
|
||||||
|
using AetherBags.Inventory;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
using KamiToolKit.Classes;
|
||||||
|
using KamiToolKit.Nodes;
|
||||||
|
|
||||||
|
namespace AetherBags.Nodes;
|
||||||
|
|
||||||
|
public class InventoryCategoryNode : SimpleComponentNode
|
||||||
|
{
|
||||||
|
private readonly TextNode _categoryNameTextNode;
|
||||||
|
private readonly HybridDirectionalFlexNode<DragDropNode> _itemGridNode;
|
||||||
|
public InventoryCategoryNode()
|
||||||
|
{
|
||||||
|
_categoryNameTextNode = new TextNode
|
||||||
|
{
|
||||||
|
Size = new Vector2(100, 14),
|
||||||
|
AlignmentType = AlignmentType.Left
|
||||||
|
};
|
||||||
|
_categoryNameTextNode.AttachNode(this);
|
||||||
|
|
||||||
|
_itemGridNode = new HybridDirectionalFlexNode<DragDropNode>
|
||||||
|
{
|
||||||
|
Position = new Vector2(0, 16),
|
||||||
|
Size = new Vector2(240, 100),
|
||||||
|
FillRowsFirst = true,
|
||||||
|
ItemsPerLine = 10,
|
||||||
|
HorizontalPadding = 6,
|
||||||
|
VerticalPadding = 6,
|
||||||
|
};
|
||||||
|
_itemGridNode.AttachNode(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public required CategoryInfo Category
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
|
||||||
|
_categoryNameTextNode.String = value.Name;
|
||||||
|
_categoryNameTextNode.TextColor = value.Color;
|
||||||
|
_categoryNameTextNode.TooltipString = value.Description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public required List<ItemInfo> Items
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
|
||||||
|
UpdateItemGrid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool UpdateItemGrid()
|
||||||
|
{
|
||||||
|
var listUpdated = _itemGridNode.SyncWithListData(Items, node => node.ItemInfo, data => CreateInventoryDragDropNode(data));
|
||||||
|
return listUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe InventoryDragDropNode CreateInventoryDragDropNode(ItemInfo data)
|
||||||
|
{
|
||||||
|
InventoryDragDropNode node = new InventoryDragDropNode
|
||||||
|
{
|
||||||
|
Size = new Vector2(40),
|
||||||
|
IsVisible = true,
|
||||||
|
IconId = data.IconId,
|
||||||
|
AcceptedType = DragDropType.Nothing,
|
||||||
|
IsDraggable = false,
|
||||||
|
Payload = new DragDropPayload
|
||||||
|
{
|
||||||
|
Type = DragDropType.Item,
|
||||||
|
Int1 = (int)data.Item.Container,
|
||||||
|
Int2 = (int)data.Item.ItemId,
|
||||||
|
},
|
||||||
|
IsClickable = true,
|
||||||
|
OnRollOver = node => node.ShowInventoryItemTooltip(data.Item.Container, data.Item.Slot),
|
||||||
|
OnRollOut = node => node.HideTooltip(),
|
||||||
|
ItemInfo = data
|
||||||
|
};
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using AetherBags.Extensions;
|
||||||
|
using AetherBags.Inventory;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
using KamiToolKit.Classes;
|
||||||
|
using KamiToolKit.Nodes;
|
||||||
|
|
||||||
|
namespace AetherBags.Nodes;
|
||||||
|
|
||||||
|
public class InventoryDragDropNode : DragDropNode
|
||||||
|
{
|
||||||
|
private readonly TextNode _quantityTextNode;
|
||||||
|
public unsafe InventoryDragDropNode()
|
||||||
|
{
|
||||||
|
_quantityTextNode = new TextNode {
|
||||||
|
Size = new Vector2(40.0f, 12.0f),
|
||||||
|
Position = new Vector2(4.0f, 34.0f),
|
||||||
|
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||||
|
Color = ColorHelper.GetColor(50),
|
||||||
|
TextOutlineColor = ColorHelper.GetColor(51),
|
||||||
|
TextFlags = TextFlags.Edge,
|
||||||
|
AlignmentType = AlignmentType.Right,
|
||||||
|
};
|
||||||
|
_quantityTextNode.AttachNode(this);
|
||||||
|
CollisionNode.AddEvent(AtkEventType.MouseDown, OnItemMouseDown);
|
||||||
|
CollisionNode.AddEvent(AtkEventType.MouseClick, OnItemClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public required ItemInfo ItemInfo
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
_quantityTextNode.String = value.ItemCount.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void OnItemMouseDown(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
|
||||||
|
InventoryItem item = ItemInfo.Item;
|
||||||
|
if (!atkEventData->IsRightClick) return;
|
||||||
|
|
||||||
|
AgentInventoryContext* context = AgentInventoryContext.Instance();
|
||||||
|
context->OpenForItemSlot(item.Container, item.Slot, 0, context->AddonId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void OnItemClicked(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) {
|
||||||
|
InventoryItem item = ItemInfo.Item;
|
||||||
|
if (!atkEventData->IsLeftClick) return;
|
||||||
|
item.UseItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
-5
@@ -1,27 +1,77 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using AetherBags.Addons;
|
||||||
|
using AetherBags.Helpers;
|
||||||
|
using Dalamud.Game.Addon.Lifecycle;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
|
using KamiToolKit;
|
||||||
|
|
||||||
namespace AetherBags;
|
namespace AetherBags;
|
||||||
|
|
||||||
public class Plugin : IDalamudPlugin
|
public class Plugin : IDalamudPlugin
|
||||||
{
|
{
|
||||||
public const string CommandName = "/aetherbags";
|
private static string HelpDescription => "Opens your inventory.";
|
||||||
|
|
||||||
public Plugin(IDalamudPluginInterface pluginInterface)
|
public Plugin(IDalamudPluginInterface pluginInterface)
|
||||||
{
|
{
|
||||||
pluginInterface.Create<Services>();
|
pluginInterface.Create<Services>();
|
||||||
|
|
||||||
Services.CommandManager.AddHandler(CommandName, new CommandInfo(this.OnCommand)
|
BackupHelper.DoConfigBackup(pluginInterface);
|
||||||
|
|
||||||
|
KamiToolKitLibrary.Initialize(pluginInterface);
|
||||||
|
|
||||||
|
System.AddonInventoryWindow = new AddonInventoryWindow
|
||||||
{
|
{
|
||||||
HelpMessage = ""
|
InternalName = "AetherBags",
|
||||||
|
Title = "AetherBags",
|
||||||
|
Size = new Vector2(750, 750),
|
||||||
|
};
|
||||||
|
|
||||||
|
Services.CommandManager.AddHandler("/aetherbags", new CommandInfo(OnCommand)
|
||||||
|
{
|
||||||
|
DisplayOrder = 1,
|
||||||
|
ShowInHelp = true,
|
||||||
|
HelpMessage = HelpDescription
|
||||||
});
|
});
|
||||||
|
Services.CommandManager.AddHandler("/ab", new CommandInfo(OnCommand)
|
||||||
|
{
|
||||||
|
DisplayOrder = 2,
|
||||||
|
ShowInHelp = true,
|
||||||
|
HelpMessage = HelpDescription
|
||||||
|
});
|
||||||
|
Services.ClientState.Login += OnLogin;
|
||||||
|
|
||||||
|
if (Services.ClientState.IsLoggedIn) {
|
||||||
|
Services.Framework.RunOnFrameworkThread(OnLogin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Services.CommandManager.RemoveHandler(CommandName);
|
Services.ClientState.Login -= OnLogin;
|
||||||
|
|
||||||
|
Services.CommandManager.RemoveHandler("/aetherbags");
|
||||||
|
Services.CommandManager.RemoveHandler("/ab");
|
||||||
|
|
||||||
|
KamiToolKitLibrary.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCommand(string command, string args)
|
private void OnCommand(string command, string args)
|
||||||
{
|
{
|
||||||
|
switch (command)
|
||||||
|
{
|
||||||
|
case "/aetherbags":
|
||||||
|
case "/ab":
|
||||||
|
if(args.Length == 0)
|
||||||
|
System.AddonInventoryWindow.Toggle();
|
||||||
|
if(args == "config")
|
||||||
|
System.AddonInventoryWindow.Toggle();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLogin() {
|
||||||
|
#if DEBUG
|
||||||
|
System.AddonInventoryWindow.Toggle();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,11 @@ namespace AetherBags;
|
|||||||
|
|
||||||
public class Services
|
public class Services
|
||||||
{
|
{
|
||||||
[PluginService] public static IDalamudPluginInterface PluginInterface { get; private set; } = null!;
|
[PluginService] public static IAddonLifecycle AddonLifecycle { get; set; } = null!;
|
||||||
|
[PluginService] public static IClientState ClientState { get; private set; } = null!;
|
||||||
[PluginService] public static ICommandManager CommandManager { get; private set; } = null!;
|
[PluginService] public static ICommandManager CommandManager { get; private set; } = null!;
|
||||||
|
[PluginService] public static IDataManager DataManager { get; set; } = null!;
|
||||||
|
[PluginService] public static IDalamudPluginInterface PluginInterface { get; private set; } = null!;
|
||||||
|
[PluginService] public static IFramework Framework { get; private set; } = null!;
|
||||||
|
[PluginService] public static IPluginLog Logger { get; private set; } = null!;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using AetherBags.Addons;
|
||||||
|
|
||||||
|
namespace AetherBags;
|
||||||
|
|
||||||
|
public static class System
|
||||||
|
{
|
||||||
|
public static AddonInventoryWindow AddonInventoryWindow { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"net10.0-windows7.0": {
|
||||||
|
"DalamudPackager": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[14.0.1, )",
|
||||||
|
"resolved": "14.0.1",
|
||||||
|
"contentHash": "y0WWyUE6dhpGdolK3iKgwys05/nZaVf4ZPtIjpLhJBZvHxkkiE23zYRo7K7uqAgoK/QvK5cqF6l3VG5AbgC6KA=="
|
||||||
|
},
|
||||||
|
"DotNet.ReproducibleBuilds": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[1.2.39, )",
|
||||||
|
"resolved": "1.2.39",
|
||||||
|
"contentHash": "fcFN01tDTIQqDuTwr1jUQK/geofiwjG5DycJQOnC72i1SsLAk1ELe+apBOuZ11UMQG8YKFZG1FgvjZPbqHyatg=="
|
||||||
|
},
|
||||||
|
"SixLabors.ImageSharp": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "3.1.12",
|
||||||
|
"contentHash": "iAg6zifihXEFS/t7fiHhZBGAdCp3FavsF4i2ZIDp0JfeYeDVzvmlbY1CNhhIKimaIzrzSi5M/NBFcWvZT2rB/A=="
|
||||||
|
},
|
||||||
|
"kamitoolkit": {
|
||||||
|
"type": "Project",
|
||||||
|
"dependencies": {
|
||||||
|
"SixLabors.ImageSharp": "[3.1.12, )"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule
+1
Submodule KamiToolKit added at 44ac1e0c3a
Reference in New Issue
Block a user