Very basic IPC AT support
This commit is contained in:
@@ -8,8 +8,10 @@ namespace AetherBags.Configuration;
|
||||
|
||||
public class CategorySettings
|
||||
{
|
||||
public bool CategoriesEnabled { get; set; } = true;
|
||||
public bool GameCategoriesEnabled { get; set; } = true;
|
||||
public bool UserCategoriesEnabled { get; set; } = true;
|
||||
public bool AllaganToolsCategoriesEnabled { get; set; } = false;
|
||||
|
||||
public List<UserCategoryDefinition> UserCategories { get; set; } = new();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Plugin.Ipc;
|
||||
|
||||
namespace AetherBags.IPC;
|
||||
|
||||
public class AllaganToolsIPC : IDisposable
|
||||
{
|
||||
private ICallGateSubscriber<bool>? _isInitialized;
|
||||
private ICallGateSubscriber<bool, bool>? _initialized;
|
||||
private ICallGateSubscriber<string, Dictionary<uint, uint>>? _getFilterItems;
|
||||
private ICallGateSubscriber<Dictionary<string, string>>? _getSearchFilters;
|
||||
private ICallGateSubscriber<string, bool>? _enableUiFilter;
|
||||
private ICallGateSubscriber<string, bool>? _toggleUiFilter;
|
||||
|
||||
public bool IsReady { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cached filter items. Key = filterKey, Value = (ItemId -> Quantity).
|
||||
/// </summary>
|
||||
public Dictionary<string, Dictionary<uint, uint>> CachedFilterItems { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Cached search filters. Key -> Name.
|
||||
/// </summary>
|
||||
public Dictionary<string, string> CachedSearchFilters { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Quick lookup: ItemId -> List of filter keys that contain this item.
|
||||
/// </summary>
|
||||
public Dictionary<uint, List<string>> ItemToFilters { get; } = new();
|
||||
|
||||
public event Action? OnInitialized;
|
||||
public event Action? OnFiltersRefreshed;
|
||||
|
||||
public AllaganToolsIPC()
|
||||
{
|
||||
try
|
||||
{
|
||||
_isInitialized = Services.PluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
|
||||
_initialized = Services.PluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
|
||||
_getFilterItems = Services.PluginInterface.GetIpcSubscriber<string, Dictionary<uint, uint>>("AllaganTools.GetFilterItems");
|
||||
_getSearchFilters = Services.PluginInterface.GetIpcSubscriber<Dictionary<string, string>>("AllaganTools.GetSearchFilters");
|
||||
_enableUiFilter = Services.PluginInterface.GetIpcSubscriber<string, bool>("AllaganTools.EnableUiFilter");
|
||||
_toggleUiFilter = Services.PluginInterface.GetIpcSubscriber<string, bool>("AllaganTools.ToggleUiFilter");
|
||||
|
||||
_initialized.Subscribe(OnAllaganInitialized);
|
||||
|
||||
// Check if already initialized
|
||||
try
|
||||
{
|
||||
IsReady = _isInitialized.InvokeFunc();
|
||||
if (IsReady)
|
||||
{
|
||||
RefreshFilters();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
IsReady = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.Logger.Debug($"Allagan Tools not available: {ex.Message}");
|
||||
IsReady = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAllaganInitialized(bool initialized)
|
||||
{
|
||||
IsReady = initialized;
|
||||
if (initialized)
|
||||
{
|
||||
Services.Logger.Information("Allagan Tools IPC connected");
|
||||
RefreshFilters();
|
||||
OnInitialized?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes all cached filter data from Allagan Tools.
|
||||
/// Call this when you need updated filter information.
|
||||
/// </summary>
|
||||
public void RefreshFilters()
|
||||
{
|
||||
if (!IsReady) return;
|
||||
|
||||
try
|
||||
{
|
||||
CachedSearchFilters.Clear();
|
||||
CachedFilterItems.Clear();
|
||||
ItemToFilters.Clear();
|
||||
|
||||
var filters = _getSearchFilters?.InvokeFunc();
|
||||
if (filters == null) return;
|
||||
|
||||
foreach (var (key, name) in filters)
|
||||
{
|
||||
CachedSearchFilters[key] = name;
|
||||
|
||||
var items = _getFilterItems?.InvokeFunc(key);
|
||||
if (items != null && items.Count > 0)
|
||||
{
|
||||
CachedFilterItems[key] = items;
|
||||
|
||||
// Build reverse lookup
|
||||
foreach (var itemId in items.Keys)
|
||||
{
|
||||
if (!ItemToFilters.TryGetValue(itemId, out var filterList))
|
||||
{
|
||||
filterList = new List<string>(capacity: 4);
|
||||
ItemToFilters[itemId] = filterList;
|
||||
}
|
||||
filterList.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Services.Logger.Debug($"Refreshed {CachedSearchFilters.Count} Allagan Tools filters, {ItemToFilters.Count} unique items");
|
||||
OnFiltersRefreshed?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.Logger.Warning($"Failed to refresh Allagan Tools filters: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an item is in any Allagan Tools filter.
|
||||
/// </summary>
|
||||
public bool IsItemInAnyFilter(uint itemId)
|
||||
=> ItemToFilters.ContainsKey(itemId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all filter keys that contain this item.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string>? GetFiltersForItem(uint itemId)
|
||||
=> ItemToFilters.TryGetValue(itemId, out var list) ? list : null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets items from a specific filter. Returns ItemId -> Quantity.
|
||||
/// </summary>
|
||||
public Dictionary<uint, uint>? GetFilterItems(string filterKey)
|
||||
{
|
||||
// Try cache first
|
||||
if (CachedFilterItems.TryGetValue(filterKey, out var cached))
|
||||
return cached;
|
||||
|
||||
if (!IsReady) return null;
|
||||
|
||||
try
|
||||
{
|
||||
return _getFilterItems?.InvokeFunc(filterKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.Logger.Warning($"GetFilterItems failed: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available search filters. Returns Key -> Name.
|
||||
/// </summary>
|
||||
public Dictionary<string, string>? GetSearchFilters()
|
||||
{
|
||||
if (CachedSearchFilters.Count > 0)
|
||||
return CachedSearchFilters;
|
||||
|
||||
if (!IsReady) return null;
|
||||
|
||||
try
|
||||
{
|
||||
return _getSearchFilters?.InvokeFunc();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.Logger.Warning($"GetSearchFilters failed: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_initialized?.Unsubscribe(OnAllaganInitialized);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Ipc;
|
||||
|
||||
namespace AetherBags.IPC;
|
||||
|
||||
public class IPCService : IDisposable
|
||||
{
|
||||
public AllaganToolsIPC AllaganTools { get; }
|
||||
public WotsItIPC WotsIt { get; }
|
||||
// Future: public BiSBuddyIPC BiSBuddy { get; }
|
||||
|
||||
public IPCService()
|
||||
{
|
||||
AllaganTools = new AllaganToolsIPC();
|
||||
WotsIt = new WotsItIPC();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AllaganTools.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using Dalamud.Plugin.Ipc;
|
||||
|
||||
namespace AetherBags.IPC;
|
||||
|
||||
public class WotsItIPC : IDisposable
|
||||
{
|
||||
private ICallGateSubscriber<string, string, string, uint, string>? _registerWithSearch;
|
||||
private ICallGateSubscriber<string, bool>? _invoke;
|
||||
private ICallGateSubscriber<string, bool>? _unregisterAll;
|
||||
|
||||
private string? _searchGuid;
|
||||
|
||||
public WotsItIPC()
|
||||
{
|
||||
try
|
||||
{
|
||||
_registerWithSearch = Services.PluginInterface.GetIpcSubscriber<string, string, string, uint, string>("FA.RegisterWithSearch");
|
||||
_unregisterAll = Services.PluginInterface.GetIpcSubscriber<string, bool>("FA.UnregisterAll");
|
||||
_invoke = Services.PluginInterface.GetIpcSubscriber<string, bool>("FA.Invoke");
|
||||
|
||||
_invoke.Subscribe(OnInvoke);
|
||||
|
||||
Register();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.Logger.Debug($"WotsIt not available: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void Register()
|
||||
{
|
||||
try
|
||||
{
|
||||
UnregisterAll();
|
||||
|
||||
_searchGuid = _registerWithSearch?.InvokeFunc(
|
||||
Services.PluginInterface.InternalName,
|
||||
"AetherBags: Search Inventory",
|
||||
"AetherBags Search",
|
||||
66472 // Icon ID
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Services.Logger.Debug($"Failed to register with WotsIt: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInvoke(string guid)
|
||||
{
|
||||
if (guid == _searchGuid)
|
||||
{
|
||||
if (! System.AddonInventoryWindow.IsOpen)
|
||||
{
|
||||
System.AddonInventoryWindow.Open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool UnregisterAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
_unregisterAll?.InvokeFunc(Services.PluginInterface.InternalName);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_invoke?.Unsubscribe(OnInvoke);
|
||||
UnregisterAll();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AetherBags.Configuration;
|
||||
using AetherBags.Inventory.Items;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
namespace AetherBags.Inventory.Categories;
|
||||
|
||||
@@ -18,6 +19,15 @@ public static class CategoryBucketManager
|
||||
public static bool IsUserCategoryKey(uint key)
|
||||
=> (key & UserCategoryKeyFlag) != 0;
|
||||
|
||||
private const uint AllaganFilterKeyFlag = 0x4000_0000;
|
||||
|
||||
public static uint MakeAllaganFilterKey(int index)
|
||||
=> AllaganFilterKeyFlag | (uint)(index & 0x3FFF_FFFF);
|
||||
|
||||
public static bool IsAllaganFilterKey(uint key)
|
||||
=> (key & AllaganFilterKeyFlag) != 0 && (key & UserCategoryKeyFlag) == 0;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resets all buckets for a new refresh cycle.
|
||||
/// </summary>
|
||||
@@ -148,6 +158,74 @@ public static class CategoryBucketManager
|
||||
}
|
||||
}
|
||||
|
||||
public static void BucketByAllaganFilters(
|
||||
Dictionary<ulong, ItemInfo> itemInfoByKey,
|
||||
Dictionary<uint, CategoryBucket> bucketsByKey,
|
||||
HashSet<ulong> claimedKeys,
|
||||
bool allaganCategoriesEnabled)
|
||||
{
|
||||
if (!allaganCategoriesEnabled) return;
|
||||
if (! System.IPC.AllaganTools.IsReady) return;
|
||||
|
||||
var filters = System.IPC.AllaganTools.CachedSearchFilters;
|
||||
var filterItems = System.IPC.AllaganTools.CachedFilterItems;
|
||||
|
||||
int index = 0;
|
||||
foreach (var (filterKey, filterName) in filters)
|
||||
{
|
||||
if (!filterItems. TryGetValue(filterKey, out var itemIds))
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint bucketKey = MakeAllaganFilterKey(index);
|
||||
|
||||
if (!bucketsByKey.TryGetValue(bucketKey, out CategoryBucket? bucket))
|
||||
{
|
||||
bucket = new CategoryBucket
|
||||
{
|
||||
Key = bucketKey,
|
||||
Category = new CategoryInfo
|
||||
{
|
||||
Name = $"[AT] {filterName}",
|
||||
Description = $"Allagan Tools filter: {filterName}",
|
||||
Color = ColorHelper.GetColor(32),
|
||||
},
|
||||
Items = new List<ItemInfo>(capacity: 16),
|
||||
FilteredItems = new List<ItemInfo>(capacity: 16),
|
||||
Used = true,
|
||||
};
|
||||
bucketsByKey. Add(bucketKey, bucket);
|
||||
}
|
||||
else
|
||||
{
|
||||
bucket.Used = true;
|
||||
bucket.Category.Name = $"[AT] {filterName}";
|
||||
}
|
||||
|
||||
foreach (var itemKvp in itemInfoByKey)
|
||||
{
|
||||
ulong itemKey = itemKvp.Key;
|
||||
ItemInfo item = itemKvp.Value;
|
||||
|
||||
if (claimedKeys.Contains(itemKey))
|
||||
continue;
|
||||
|
||||
if (itemIds.ContainsKey(item.Item.ItemId))
|
||||
{
|
||||
bucket.Items.Add(item);
|
||||
claimedKeys.Add(itemKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (bucket.Items. Count == 0)
|
||||
bucket.Used = false;
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void BucketUnclaimedToMisc(
|
||||
Dictionary<ulong, ItemInfo> itemInfoByKey,
|
||||
Dictionary<uint, CategoryBucket> bucketsByKey,
|
||||
@@ -215,9 +293,13 @@ public static class CategoryBucketManager
|
||||
|
||||
sortedCategoryKeys.Sort((left, right) =>
|
||||
{
|
||||
bool leftCategory = IsUserCategoryKey(left);
|
||||
bool rightCategory = IsUserCategoryKey(right);
|
||||
if (leftCategory != rightCategory) return leftCategory ? -1 : 1;
|
||||
bool leftUser = IsUserCategoryKey(left);
|
||||
bool rightUser = IsUserCategoryKey(right);
|
||||
bool leftAllagan = IsAllaganFilterKey(left);
|
||||
bool rightAllagan = IsAllaganFilterKey(right);
|
||||
if (leftUser != rightUser) return leftUser ? -1 : 1;
|
||||
if (leftAllagan != rightAllagan) return leftAllagan ? -1 : 1;
|
||||
|
||||
return left.CompareTo(right);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Linq;
|
||||
using AetherBags.Configuration;
|
||||
using AetherBags.Currency;
|
||||
using AetherBags.Inventory.Categories;
|
||||
using AetherBags.Inventory.Context;
|
||||
using AetherBags.Inventory.Items;
|
||||
using AetherBags.Inventory.Scanning;
|
||||
using Dalamud.Game.Inventory;
|
||||
@@ -32,68 +31,6 @@ public static unsafe class InventoryState
|
||||
public static bool Contains(this IReadOnlyCollection<InventoryType> inventoryTypes, GameInventoryType type)
|
||||
=> inventoryTypes.Contains((InventoryType)type);
|
||||
|
||||
public static void RefreshFromGame()
|
||||
{
|
||||
FFXIVClientStructs.FFXIV.Client.Game.InventoryManager* inventoryManager = FFXIVClientStructs.FFXIV.Client.Game.InventoryManager.Instance();
|
||||
if (inventoryManager == null)
|
||||
{
|
||||
ClearAll();
|
||||
return;
|
||||
}
|
||||
|
||||
var config = System.Config;
|
||||
InventoryStackMode stackMode = config.General.StackMode;
|
||||
bool userCategoriesEnabled = config.Categories.UserCategoriesEnabled;
|
||||
bool gameCategoriesEnabled = config.Categories.GameCategoriesEnabled;
|
||||
List<UserCategoryDefinition> userCategories = config.Categories.UserCategories.Where(category => category.Enabled).ToList();
|
||||
|
||||
Services.Logger.DebugOnly($"RefreshFromGame StackMode={stackMode}");
|
||||
|
||||
AggByKey.Clear();
|
||||
ItemInfoByKey.Clear();
|
||||
SortedCategoryKeys.Clear();
|
||||
AllCategories.Clear();
|
||||
FilteredCategories.Clear();
|
||||
ClaimedKeys.Clear();
|
||||
|
||||
InventoryScanner.ScanBags(inventoryManager, stackMode, AggByKey);
|
||||
CategoryBucketManager.ResetBuckets(BucketsByKey);
|
||||
InventoryScanner.BuildItemInfos(AggByKey, ItemInfoByKey);
|
||||
InventoryContextState.RefreshMaps();
|
||||
InventoryContextState.RefreshBlockedSlots();
|
||||
|
||||
if (userCategoriesEnabled && userCategories.Count > 0)
|
||||
{
|
||||
CategoryBucketManager.BucketByUserCategories(
|
||||
ItemInfoByKey,
|
||||
userCategories,
|
||||
BucketsByKey,
|
||||
ClaimedKeys,
|
||||
UserCategoriesSortedScratch);
|
||||
}
|
||||
|
||||
if (gameCategoriesEnabled)
|
||||
{
|
||||
CategoryBucketManager.BucketByGameCategories(
|
||||
ItemInfoByKey,
|
||||
BucketsByKey,
|
||||
ClaimedKeys,
|
||||
userCategoriesEnabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
CategoryBucketManager.BucketUnclaimedToMisc(
|
||||
ItemInfoByKey,
|
||||
BucketsByKey,
|
||||
ClaimedKeys,
|
||||
userCategoriesEnabled);
|
||||
}
|
||||
|
||||
InventoryScanner.PruneStaleItemInfos(AggByKey, ItemInfoByKey, RemoveKeysScratch);
|
||||
CategoryBucketManager.SortBucketsAndBuildKeyList(BucketsByKey, SortedCategoryKeys);
|
||||
CategoryBucketManager.BuildCategorizedList(BucketsByKey, SortedCategoryKeys, AllCategories);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<CategorizedInventory> GetInventoryItemCategories(string filterString = "", bool invert = false)
|
||||
{
|
||||
return InventoryFilter.FilterCategories(
|
||||
|
||||
@@ -25,14 +25,14 @@ public abstract class InventoryStateBase
|
||||
|
||||
public virtual unsafe void RefreshFromGame()
|
||||
{
|
||||
FFXIVClientStructs.FFXIV.Client.Game.InventoryManager* inventoryManager = FFXIVClientStructs.FFXIV.Client.Game.InventoryManager.Instance();
|
||||
InventoryManager* inventoryManager = InventoryManager.Instance();
|
||||
if (inventoryManager == null)
|
||||
{
|
||||
ClearAll();
|
||||
return;
|
||||
}
|
||||
|
||||
var config = AetherBags.System.Config;
|
||||
var config = System.Config;
|
||||
InventoryStackMode stackMode = config.General.StackMode;
|
||||
|
||||
AggByKey.Clear();
|
||||
@@ -61,8 +61,10 @@ public abstract class InventoryStateBase
|
||||
|
||||
protected virtual void ApplyCategories(SystemConfiguration config)
|
||||
{
|
||||
bool userCategoriesEnabled = config.Categories.UserCategoriesEnabled;
|
||||
bool gameCategoriesEnabled = config.Categories.GameCategoriesEnabled;
|
||||
bool categoriesEnabled = config.Categories.CategoriesEnabled;
|
||||
bool userCategoriesEnabled = config.Categories.UserCategoriesEnabled && categoriesEnabled;
|
||||
bool gameCategoriesEnabled = config.Categories.GameCategoriesEnabled && categoriesEnabled;
|
||||
bool allaganCategoriesEnabled = config.Categories.AllaganToolsCategoriesEnabled && categoriesEnabled;
|
||||
var userCategories = config.Categories.UserCategories.Where(c => c.Enabled).ToList();
|
||||
|
||||
if (userCategoriesEnabled && userCategories.Count > 0)
|
||||
@@ -71,6 +73,12 @@ public abstract class InventoryStateBase
|
||||
ItemInfoByKey, userCategories, BucketsByKey, ClaimedKeys, UserCategoriesSortedScratch);
|
||||
}
|
||||
|
||||
if (allaganCategoriesEnabled)
|
||||
{
|
||||
CategoryBucketManager.BucketByAllaganFilters(
|
||||
ItemInfoByKey, BucketsByKey, ClaimedKeys, allaganCategoriesEnabled);
|
||||
}
|
||||
|
||||
if (gameCategoriesEnabled)
|
||||
{
|
||||
CategoryBucketManager.BucketByGameCategories(
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using System.Numerics;
|
||||
using AetherBags.Configuration;
|
||||
using AetherBags.Inventory;
|
||||
using AetherBags.Nodes.Color;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes.Configuration.Category;
|
||||
|
||||
public sealed class CategoryGeneralConfigurationNode : TabbedVerticalListNode
|
||||
{
|
||||
private readonly CheckboxNode _allaganToolsCheckbox;
|
||||
public CategoryGeneralConfigurationNode()
|
||||
{
|
||||
CategorySettings config = System.Config.Categories;
|
||||
|
||||
LabelTextNode titleNode = new LabelTextNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
String = "Category Configuration",
|
||||
TextColor = ColorHelper.GetColor(2),
|
||||
TextOutlineColor = ColorHelper.GetColor(0),
|
||||
};
|
||||
AddNode(titleNode);
|
||||
|
||||
AddTab(1);
|
||||
|
||||
CheckboxNode categoriesEnabled = new CheckboxNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
IsVisible = true,
|
||||
String = "Categories Enabled",
|
||||
IsChecked = config.CategoriesEnabled,
|
||||
OnClick = isChecked =>
|
||||
{
|
||||
config.CategoriesEnabled = isChecked;
|
||||
RefreshInventory();
|
||||
}
|
||||
};
|
||||
AddNode(categoriesEnabled);
|
||||
|
||||
AddTab(1);
|
||||
|
||||
CheckboxNode gameCategoriesEnabled = new CheckboxNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
IsVisible = true,
|
||||
String = "Game Categories",
|
||||
IsChecked = config.GameCategoriesEnabled,
|
||||
TextTooltip = "Use the game's built-in item categories (e.g., Arms, Tools, Armor).",
|
||||
OnClick = isChecked =>
|
||||
{
|
||||
config.GameCategoriesEnabled = isChecked;
|
||||
RefreshInventory();
|
||||
}
|
||||
};
|
||||
AddNode(gameCategoriesEnabled);
|
||||
|
||||
CheckboxNode userCategoriesEnabled = new CheckboxNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
IsVisible = true,
|
||||
String = "User Categories",
|
||||
IsChecked = config.UserCategoriesEnabled,
|
||||
TextTooltip = "Use your custom-defined categories.",
|
||||
OnClick = isChecked =>
|
||||
{
|
||||
config.UserCategoriesEnabled = isChecked;
|
||||
RefreshInventory();
|
||||
}
|
||||
};
|
||||
AddNode(userCategoriesEnabled);
|
||||
|
||||
bool allaganReady = System.IPC.AllaganTools?.IsReady ?? false;
|
||||
_allaganToolsCheckbox = new CheckboxNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
IsVisible = true,
|
||||
String = allaganReady ? "Allagan Tools Filters" : "Allagan Tools Filters (Not Available)",
|
||||
IsChecked = config.AllaganToolsCategoriesEnabled,
|
||||
IsEnabled = allaganReady,
|
||||
TextTooltip = allaganReady
|
||||
? "Use search filters from Allagan Tools as categories. Items matching a filter will be grouped together."
|
||||
: "Allagan Tools is not installed or not initialized.",
|
||||
OnClick = isChecked =>
|
||||
{
|
||||
config.AllaganToolsCategoriesEnabled = isChecked;
|
||||
if (isChecked)
|
||||
{
|
||||
System.IPC?.AllaganTools?.RefreshFilters();
|
||||
}
|
||||
RefreshInventory();
|
||||
}
|
||||
};
|
||||
AddNode(_allaganToolsCheckbox);
|
||||
}
|
||||
|
||||
private void RefreshInventory() => InventoryOrchestrator.RefreshAll(updateMaps: true);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes.Configuration.Category;
|
||||
|
||||
public class CategoryScrollingAreaNode : ScrollingAreaNode<VerticalListNode>
|
||||
public sealed class CategoryScrollingAreaNode : ScrollingAreaNode<VerticalListNode>
|
||||
{
|
||||
private AddonCategoryConfigurationWindow? _categoryConfigurationAddon;
|
||||
private readonly TextButtonNode _categoryConfigurationButtonNode;
|
||||
@@ -13,13 +13,15 @@ public class CategoryScrollingAreaNode : ScrollingAreaNode<VerticalListNode>
|
||||
{
|
||||
InitializeCategoryAddon();
|
||||
|
||||
ContentNode.AddNode(new CategoryGeneralConfigurationNode());
|
||||
|
||||
_categoryConfigurationButtonNode = new TextButtonNode
|
||||
{
|
||||
Size = new Vector2(300, 28),
|
||||
String = "Configure Categories",
|
||||
OnClick = () => _categoryConfigurationAddon?.Toggle(),
|
||||
};
|
||||
_categoryConfigurationButtonNode.AttachNode(this);
|
||||
ContentNode.AddNode(_categoryConfigurationButtonNode);
|
||||
}
|
||||
|
||||
private void InitializeCategoryAddon() {
|
||||
|
||||
@@ -55,8 +55,9 @@ public sealed class CurrencyGeneralConfigurationNode : TabbedVerticalListNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
IsVisible = true,
|
||||
String = "Color When Capped",
|
||||
String = "Color Weekly Cap",
|
||||
IsChecked = config.ColorWhenCapped,
|
||||
TextTooltip = "Changes the color of the currency display when you have reached the maximum amount earnable for the current week (e.g., 450/450).",
|
||||
OnClick = isChecked =>
|
||||
{
|
||||
config.ColorWhenCapped = isChecked;
|
||||
@@ -69,7 +70,7 @@ public sealed class CurrencyGeneralConfigurationNode : TabbedVerticalListNode
|
||||
|
||||
ColorInputRow cappedCurrencyColorNode = new ColorInputRow
|
||||
{
|
||||
Label = "Capped Currency Color",
|
||||
Label = "Weekly Cap Color",
|
||||
Size = new Vector2(300, 24),
|
||||
CurrentColor = config.CappedColor,
|
||||
DefaultColor = new CurrencySettings().CappedColor,
|
||||
@@ -87,8 +88,9 @@ public sealed class CurrencyGeneralConfigurationNode : TabbedVerticalListNode
|
||||
{
|
||||
Size = Size with { Y = 18 },
|
||||
IsVisible = true,
|
||||
String = "Limited Currency Color",
|
||||
String = "Color Max Capacity",
|
||||
IsChecked = config.ColorWhenLimited,
|
||||
TextTooltip = "Changes the color of the currency display when your total held amount has reached its maximum capacity (e.g., 2000/2000).",
|
||||
OnClick = isChecked =>
|
||||
{
|
||||
config.ColorWhenLimited = isChecked;
|
||||
@@ -101,7 +103,7 @@ public sealed class CurrencyGeneralConfigurationNode : TabbedVerticalListNode
|
||||
|
||||
ColorInputRow limitCurrencyColorNode = new ColorInputRow
|
||||
{
|
||||
Label = "Color Weekly Limit",
|
||||
Label = "Max Capacity Color",
|
||||
Size = new Vector2(300, 24),
|
||||
CurrentColor = config.LimitColor,
|
||||
DefaultColor = new CurrencySettings().LimitColor,
|
||||
|
||||
@@ -6,11 +6,11 @@ using AetherBags.Helpers;
|
||||
using AetherBags.Hooks;
|
||||
using AetherBags.Inventory;
|
||||
using AetherBags.Inventory.State;
|
||||
using AetherBags.IPC;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Plugin;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using KamiToolKit;
|
||||
using NotImplementedException = System.NotImplementedException;
|
||||
|
||||
namespace AetherBags;
|
||||
|
||||
@@ -31,6 +31,8 @@ public unsafe class Plugin : IDalamudPlugin
|
||||
KamiToolKitLibrary.Initialize(pluginInterface);
|
||||
System.Config = Util.LoadConfigOrDefault();
|
||||
|
||||
System.IPC = new IPCService();
|
||||
|
||||
System.AddonInventoryWindow = new AddonInventoryWindow
|
||||
{
|
||||
InternalName = "AetherBags_MainBags",
|
||||
@@ -79,6 +81,7 @@ public unsafe class Plugin : IDalamudPlugin
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
System.IPC.Dispose();
|
||||
Util.SaveConfig(System.Config);
|
||||
Services.ClientState.Login -= OnLogin;
|
||||
Services.ClientState.Logout -= OnLogout;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using AetherBags.Addons;
|
||||
using AetherBags.Configuration;
|
||||
using AetherBags.IPC;
|
||||
|
||||
namespace AetherBags;
|
||||
|
||||
@@ -9,5 +10,6 @@ public static class System
|
||||
public static AddonSaddleBagWindow AddonSaddleBagWindow { get; set; } = null!;
|
||||
public static AddonRetainerWindow AddonRetainerWindow { get; set; } = null!;
|
||||
public static AddonConfigurationWindow AddonConfigurationWindow { get; set; } = null!;
|
||||
public static IPCService IPC { get; set; } = null!;
|
||||
public static SystemConfiguration Config { get; set; } = null!;
|
||||
}
|
||||
Reference in New Issue
Block a user