Track AllaganTools filters

master v0.9
Liza 2024-03-04 12:05:46 +01:00
parent c7ceaa2cfd
commit ef1769c4f7
Signed by: liza
GPG Key ID: 7199F8D727D55F67
12 changed files with 417 additions and 139 deletions

View File

@ -15,22 +15,27 @@ internal sealed class AllaganToolsIpc : IDisposable
private readonly DalamudReflector _dalamudReflector; private readonly DalamudReflector _dalamudReflector;
private readonly IFramework _framework; private readonly IFramework _framework;
private readonly IPluginLog _pluginLog; private readonly IPluginLog _pluginLog;
private readonly ICallGateSubscriber<bool, bool>? _initalized; private readonly ICallGateSubscriber<bool, bool>? _initialized;
private readonly ICallGateSubscriber<bool>? _isInitialized; private readonly ICallGateSubscriber<bool>? _isInitialized;
private readonly ICallGateSubscriber<Dictionary<string, string>> _getSearchFilters;
public ICharacterMonitor Characters { get; private set; } = new UnavailableCharacterMonitor(); public ICharacterMonitor Characters { get; private set; } = new UnavailableCharacterMonitor();
public IInventoryMonitor Inventories { get; private set; } = new UnavailableInventoryMonitor(); public IInventoryMonitor Inventories { get; private set; } = new UnavailableInventoryMonitor();
public IFilterService Filters { get; set; } = new UnavailableFilterService();
public AllaganToolsIpc(DalamudPluginInterface pluginInterface, IChatGui chatGui, DalamudReflector dalamudReflector, IFramework framework, IPluginLog pluginLog) public AllaganToolsIpc(DalamudPluginInterface pluginInterface, IChatGui chatGui, DalamudReflector dalamudReflector,
IFramework framework, IPluginLog pluginLog)
{ {
_chatGui = chatGui; _chatGui = chatGui;
_dalamudReflector = dalamudReflector; _dalamudReflector = dalamudReflector;
_framework = framework; _framework = framework;
_pluginLog = pluginLog; _pluginLog = pluginLog;
_initalized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized"); _initialized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
_isInitialized = pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized"); _isInitialized = pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
_initalized.Subscribe(ConfigureIpc); _initialized.Subscribe(ConfigureIpc);
_getSearchFilters =
pluginInterface.GetIpcSubscriber<Dictionary<string, string>>("AllaganTools.GetSearchFilters");
try try
{ {
@ -58,6 +63,7 @@ internal sealed class AllaganToolsIpc : IDisposable
Characters = new CharacterMonitor(pluginService.GetProperty("CharacterMonitor")!.GetValue(null)!); Characters = new CharacterMonitor(pluginService.GetProperty("CharacterMonitor")!.GetValue(null)!);
Inventories = new InventoryMonitor( Inventories = new InventoryMonitor(
pluginService.GetProperty("InventoryMonitor")!.GetValue(null)!); pluginService.GetProperty("InventoryMonitor")!.GetValue(null)!);
Filters = new FilterService(pluginService.GetProperty("FilterService")!.GetValue(null)!);
} }
else else
{ {
@ -72,6 +78,32 @@ internal sealed class AllaganToolsIpc : IDisposable
}, TimeSpan.FromMilliseconds(100)); }, TimeSpan.FromMilliseconds(100));
} }
public Dictionary<string, string> GetSearchFilters()
{
try
{
return _getSearchFilters.InvokeFunc();
}
catch (IpcError e)
{
_pluginLog.Error(e, "Unable to retrieve allagantools filters");
return new Dictionary<string, string>();
}
}
public Filter? GetFilter(string keyOrName)
{
try
{
return Filters.GetFilterByKeyOrName(keyOrName);
}
catch (IpcError e)
{
_pluginLog.Error(e, $"Unable to retrieve filter items for filter '{keyOrName}'");
return null;
}
}
public Dictionary<Character, Currencies> CountCurrencies() public Dictionary<Character, Currencies> CountCurrencies()
{ {
_pluginLog.Debug($"{Characters.GetType()}, {Inventories.GetType()}"); _pluginLog.Debug($"{Characters.GetType()}, {Inventories.GetType()}");
@ -98,9 +130,10 @@ internal sealed class AllaganToolsIpc : IDisposable
public void Dispose() public void Dispose()
{ {
_initalized?.Unsubscribe(ConfigureIpc); _initialized?.Unsubscribe(ConfigureIpc);
Characters = new UnavailableCharacterMonitor(); Characters = new UnavailableCharacterMonitor();
Inventories = new UnavailableInventoryMonitor(); Inventories = new UnavailableInventoryMonitor();
Filters = new UnavailableFilterService();
} }
private sealed class InventoryWrapper private sealed class InventoryWrapper

View File

@ -0,0 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Influx.AllaganTools;
internal sealed class Filter
{
private readonly object _delegate;
private readonly MethodInfo _generateFilteredList;
public Filter(object @delegate)
{
_delegate = @delegate;
_generateFilteredList = _delegate.GetType().GetMethod("GenerateFilteredList")!;
}
public IReadOnlyList<SortingResult> GenerateFilteredList()
{
Task task = (Task)_generateFilteredList.Invoke(_delegate, new object?[] { null })!;
object result = task.GetType().GetProperty("Result")!.GetValue(task)!;
return ((IEnumerable)result.GetType().GetProperty("SortedItems")!.GetValue(result)!)
.Cast<object>()
.Select(x => new SortingResult(x))
.ToList();
}
}

View File

@ -0,0 +1,21 @@
using System.Reflection;
namespace Influx.AllaganTools;
internal sealed class FilterService : IFilterService
{
private readonly object _delegate;
private readonly MethodInfo _getFilterByKeyOrName;
public FilterService(object @delegate)
{
_delegate = @delegate;
_getFilterByKeyOrName = _delegate.GetType().GetMethod("GetFilterByKeyOrName")!;
}
public Filter? GetFilterByKeyOrName(string keyOrName)
{
var f = _getFilterByKeyOrName.Invoke(_delegate, new object?[] { keyOrName });
return f != null ? new Filter(f) : null;
}
}

View File

@ -0,0 +1,6 @@
namespace Influx.AllaganTools;
internal interface IFilterService
{
Filter? GetFilterByKeyOrName(string keyOrName);
}

View File

@ -0,0 +1,27 @@
namespace Influx.AllaganTools;
using ItemFlags = FFXIVClientStructs.FFXIV.Client.Game.InventoryItem.ItemFlags;
internal sealed class SortingResult
{
public SortingResult(object @delegate)
{
LocalContentId = (ulong)@delegate.GetType().GetProperty("SourceRetainerId")!.GetValue(@delegate)!;
Quantity = (int)@delegate.GetType().GetProperty("Quantity")!.GetValue(@delegate)!;
var inventoryItem = @delegate.GetType().GetProperty("InventoryItem")!.GetValue(@delegate)!;
ItemId = (uint)inventoryItem.GetType().GetField("ItemId")!.GetValue(inventoryItem)!;
Flags = (ItemFlags)inventoryItem.GetType().GetField("Flags")!.GetValue(inventoryItem)!;
}
public ulong LocalContentId { get; }
public uint ItemId { get; }
public ItemFlags Flags { get; }
public int Quantity { get; }
public bool IsHq => Flags.HasFlag(ItemFlags.HQ);
public override string ToString()
{
return $"{LocalContentId}, {ItemId}, {Quantity}";
}
}

View File

@ -0,0 +1,6 @@
namespace Influx.AllaganTools;
internal sealed class UnavailableFilterService : IFilterService
{
public Filter? GetFilterByKeyOrName(string keyOrName) => null;
}

View File

@ -10,6 +10,7 @@ public sealed class Configuration : IPluginConfiguration
public ServerConfiguration Server { get; set; } = new(); public ServerConfiguration Server { get; set; } = new();
public List<CharacterInfo> IncludedCharacters { get; set; } = new(); public List<CharacterInfo> IncludedCharacters { get; set; } = new();
public List<FilterInfo> IncludedInventoryFilters { get; set; } = new();
public sealed class ServerConfiguration public sealed class ServerConfiguration
{ {
@ -27,4 +28,9 @@ public sealed class Configuration : IPluginConfiguration
public string? CachedWorldName { get; set; } public string? CachedWorldName { get; set; }
public bool IncludeFreeCompany { get; set; } = true; public bool IncludeFreeCompany { get; set; } = true;
} }
public sealed class FilterInfo
{
public required string Name { get; set; }
}
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<Version>0.8</Version> <Version>0.9</Version>
<LangVersion>11.0</LangVersion> <LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -7,6 +7,7 @@ using Influx.AllaganTools;
using Influx.LocalStatistics; using Influx.LocalStatistics;
using InfluxDB.Client; using InfluxDB.Client;
using InfluxDB.Client.Api.Domain; using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Core;
using InfluxDB.Client.Writes; using InfluxDB.Client.Writes;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany; using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
@ -22,7 +23,8 @@ internal sealed class InfluxStatisticsClient : IDisposable
private readonly IPluginLog _pluginLog; private readonly IPluginLog _pluginLog;
private readonly IReadOnlyDictionary<byte, byte> _classJobToArrayIndex; private readonly IReadOnlyDictionary<byte, byte> _classJobToArrayIndex;
private readonly IReadOnlyDictionary<byte, string> _classJobNames; private readonly IReadOnlyDictionary<byte, string> _classJobNames;
private readonly Dictionary<sbyte, string> _expToJobs; private readonly IReadOnlyDictionary<sbyte, string> _expToJobs;
private readonly IReadOnlyDictionary<uint, PriceInfo> _prices;
public InfluxStatisticsClient(IChatGui chatGui, Configuration configuration, IDataManager dataManager, public InfluxStatisticsClient(IChatGui chatGui, Configuration configuration, IDataManager dataManager,
IClientState clientState, IPluginLog pluginLog) IClientState clientState, IPluginLog pluginLog)
@ -41,6 +43,13 @@ internal sealed class InfluxStatisticsClient : IDisposable
.Where(x => x.JobIndex > 0) .Where(x => x.JobIndex > 0)
.Where(x => x.Abbreviation.ToString() != "SMN") .Where(x => x.Abbreviation.ToString() != "SMN")
.ToDictionary(x => x.ExpArrayIndex, x => x.Abbreviation.ToString()); .ToDictionary(x => x.ExpArrayIndex, x => x.Abbreviation.ToString());
_prices = dataManager.GetExcelSheet<Item>()!
.ToDictionary(x => x.RowId, x => new PriceInfo
{
Name = x.Name.ToString(),
Normal = x.PriceLow,
UiCategory = x.ItemUICategory.Row,
});
} }
public bool Enabled => _configuration.Server.Enabled && public bool Enabled => _configuration.Server.Enabled &&
@ -84,143 +93,16 @@ internal sealed class InfluxStatisticsClient : IDisposable
{ {
if (character.CharacterType == CharacterType.Character) if (character.CharacterType == CharacterType.Character)
{ {
update.LocalStats.TryGetValue(character, out LocalStats? localStats); values.AddRange(GenerateCharacterStats(character, currencies, update, date));
bool includeFc = character.FreeCompanyId > 0 &&
_configuration.IncludedCharacters.Any(x =>
x.LocalContentId == character.CharacterId && x.IncludeFreeCompany);
values.Add(PointData.Measurement("currency")
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", character.Name)
.Tag("type", character.CharacterType.ToString())
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
.Field("gil", localStats?.Gil ?? currencies.Gil)
.Field("mgp", localStats?.MGP ?? 0)
.Field("ventures", currencies.Ventures)
.Field("ceruleum_tanks", currencies.CeruleumTanks)
.Field("repair_kits", currencies.RepairKits)
.Timestamp(date, WritePrecision.S));
if (localStats != null)
{
values.Add(PointData.Measurement("grandcompany")
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", character.Name)
.Tag("type", character.CharacterType.ToString())
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
.Field("gc", localStats.GrandCompany)
.Field("gc_rank", localStats.GcRank)
.Field("seals", (GrandCompany)localStats.GrandCompany switch
{
GrandCompany.Maelstrom => currencies.GcSealsMaelstrom,
GrandCompany.TwinAdder => currencies.GcSealsTwinAdders,
GrandCompany.ImmortalFlames => currencies.GcSealsImmortalFlames,
_ => 0,
})
.Field("seal_cap", localStats.GcRank switch
{
1 => 10_000,
2 => 15_000,
3 => 20_000,
4 => 25_000,
5 => 30_000,
6 => 35_000,
7 => 40_000,
8 => 45_000,
9 => 50_000,
10 => 80_000,
11 => 90_000,
_ => 0,
})
.Field("squadron_unlocked", localStats.SquadronUnlocked ? 1 : 0)
.Timestamp(date, WritePrecision.S));
if (localStats.ClassJobLevels.Count > 0)
{
foreach (var (expIndex, abbreviation) in _expToJobs)
{
var level = localStats.ClassJobLevels[expIndex];
if (level > 0)
{
values.Add(PointData.Measurement("experience")
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", character.Name)
.Tag("type", character.CharacterType.ToString())
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
.Tag("job", abbreviation)
.Field("level", level)
.Timestamp(date, WritePrecision.S));
}
}
}
if (localStats.MsqCount != -1)
{
values.Add(PointData.Measurement("quests")
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", character.Name)
.Tag("msq_name", localStats.MsqName)
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
.Field("msq_count", localStats.MsqCount)
.Field("msq_genre", localStats.MsqGenre)
.Timestamp(date, WritePrecision.S));
}
}
} }
else if (character.CharacterType == CharacterType.Retainer) else if (character.CharacterType == CharacterType.Retainer)
{ {
var owner = currencyStats.Keys.First(x => x.CharacterId == character.OwnerId); values.AddRange(GenerateRetainerStats(character, currencies, update, date));
values.Add(PointData.Measurement("currency")
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", owner.Name)
.Tag("player_id", character.OwnerId.ToString())
.Tag("type", character.CharacterType.ToString())
.Tag("retainer_name", character.Name)
.Field("gil", currencies.Gil)
.Field("ceruleum_tanks", currencies.CeruleumTanks)
.Field("repair_kits", currencies.RepairKits)
.Timestamp(date, WritePrecision.S));
if (update.LocalStats.TryGetValue(owner, out var ownerStats) && character.ClassJob != 0)
{
values.Add(PointData.Measurement("retainer")
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", owner.Name)
.Tag("player_id", character.OwnerId.ToString())
.Tag("type", character.CharacterType.ToString())
.Tag("retainer_name", character.Name)
.Tag("class", _classJobNames[character.ClassJob])
.Field("level", character.Level)
.Field("is_max_level", character.Level == ownerStats.MaxLevel ? 1 : 0)
.Field("can_reach_max_level",
ownerStats.ClassJobLevels.Count > 0 &&
ownerStats.ClassJobLevels[_classJobToArrayIndex[character.ClassJob]] ==
ownerStats.MaxLevel
? 1
: 0)
.Field("levels_before_cap",
ownerStats.ClassJobLevels.Count > 0
? ownerStats.ClassJobLevels[_classJobToArrayIndex[character.ClassJob]] -
character.Level
: 0)
.Timestamp(date, WritePrecision.S));
}
} }
else if (character.CharacterType == CharacterType.FreeCompanyChest && else if (character.CharacterType == CharacterType.FreeCompanyChest &&
validFcIds.Contains(character.CharacterId)) validFcIds.Contains(character.CharacterId))
{ {
update.FcStats.TryGetValue(character.CharacterId, out FcStats? fcStats); values.AddRange(GenerateFcStats(character, currencies, update, date));
values.Add(PointData.Measurement("currency")
.Tag("id", character.CharacterId.ToString())
.Tag("fc_name", character.Name)
.Tag("type", character.CharacterType.ToString())
.Field("gil", currencies.Gil)
.Field("fccredit", fcStats?.FcCredits ?? 0)
.Field("ceruleum_tanks", currencies.CeruleumTanks)
.Field("repair_kits", currencies.RepairKits)
.Timestamp(date, WritePrecision.S));
} }
} }
@ -263,8 +145,186 @@ internal sealed class InfluxStatisticsClient : IDisposable
}); });
} }
private IEnumerable<PointData> GenerateCharacterStats(Character character, Currencies currencies,
StatisticsUpdate update, DateTime date)
{
update.LocalStats.TryGetValue(character, out LocalStats? localStats);
bool includeFc = character.FreeCompanyId > 0 &&
_configuration.IncludedCharacters.Any(x =>
x.LocalContentId == character.CharacterId && x.IncludeFreeCompany);
Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", character.Name)
.Tag("type", character.CharacterType.ToString())
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
.Timestamp(date, WritePrecision.S);
yield return pointData("currency")
.Field("gil", localStats?.Gil ?? currencies.Gil)
.Field("mgp", localStats?.MGP ?? 0)
.Field("ventures", currencies.Ventures)
.Field("ceruleum_tanks", currencies.CeruleumTanks)
.Field("repair_kits", currencies.RepairKits);
if (localStats != null)
{
yield return pointData("grandcompany")
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", character.Name)
.Tag("type", character.CharacterType.ToString())
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
.Field("gc", localStats.GrandCompany)
.Field("gc_rank", localStats.GcRank)
.Field("seals", (GrandCompany)localStats.GrandCompany switch
{
GrandCompany.Maelstrom => currencies.GcSealsMaelstrom,
GrandCompany.TwinAdder => currencies.GcSealsTwinAdders,
GrandCompany.ImmortalFlames => currencies.GcSealsImmortalFlames,
_ => 0,
})
.Field("seal_cap", localStats.GcRank switch
{
1 => 10_000,
2 => 15_000,
3 => 20_000,
4 => 25_000,
5 => 30_000,
6 => 35_000,
7 => 40_000,
8 => 45_000,
9 => 50_000,
10 => 80_000,
11 => 90_000,
_ => 0,
})
.Field("squadron_unlocked", localStats.SquadronUnlocked ? 1 : 0);
if (localStats.ClassJobLevels.Count > 0)
{
foreach (var (expIndex, abbreviation) in _expToJobs)
{
var level = localStats.ClassJobLevels[expIndex];
if (level > 0)
{
yield return pointData("experience")
.Tag("job", abbreviation)
.Field("level", level);
}
}
}
if (localStats.MsqCount != -1)
{
yield return pointData("quests")
.Tag("msq_name", localStats.MsqName)
.Field("msq_count", localStats.MsqCount)
.Field("msq_genre", localStats.MsqGenre);
}
}
foreach (var inventoryPoint in GenerateInventoryStats(character.CharacterId, update, pointData))
yield return inventoryPoint;
}
private IEnumerable<PointData> GenerateRetainerStats(Character character, Currencies currencies,
StatisticsUpdate update, DateTime date)
{
var owner = update.Currencies.Keys.First(x => x.CharacterId == character.OwnerId);
Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", owner.Name)
.Tag("player_id", character.OwnerId.ToString())
.Tag("type", character.CharacterType.ToString())
.Tag("retainer_name", character.Name)
.Timestamp(date, WritePrecision.S);
yield return pointData("currency")
.Field("gil", currencies.Gil)
.Field("ceruleum_tanks", currencies.CeruleumTanks)
.Field("repair_kits", currencies.RepairKits);
if (update.LocalStats.TryGetValue(owner, out var ownerStats) && character.ClassJob != 0)
{
yield return pointData("retainer")
.Tag("class", _classJobNames[character.ClassJob])
.Field("level", character.Level)
.Field("is_max_level", character.Level == ownerStats.MaxLevel ? 1 : 0)
.Field("can_reach_max_level",
ownerStats.ClassJobLevels.Count > 0 &&
ownerStats.ClassJobLevels[_classJobToArrayIndex[character.ClassJob]] ==
ownerStats.MaxLevel
? 1
: 0)
.Field("levels_before_cap",
ownerStats.ClassJobLevels.Count > 0
? ownerStats.ClassJobLevels[_classJobToArrayIndex[character.ClassJob]] -
character.Level
: 0);
}
foreach (var inventoryPoint in GenerateInventoryStats(character.CharacterId, update, pointData))
yield return inventoryPoint;
}
private IEnumerable<PointData> GenerateInventoryStats(ulong localContentId, StatisticsUpdate update,
Func<string, PointData> pointData)
{
foreach (var (filterName, items) in update.InventoryItems)
{
foreach (var item in items.Where(x => x.LocalContentId == localContentId)
.GroupBy(x => new { x.ItemId, x.IsHq }))
{
_prices.TryGetValue(item.Key.ItemId, out PriceInfo priceInfo);
_pluginLog.Information($"CH {localContentId} → {priceInfo.Name} x {item.Sum(x => x.Quantity)}");
bool priceHq = item.Key.IsHq || priceInfo.UiCategory == 58; // materia always uses HQ prices
yield return pointData("items")
.Tag("filter_name", filterName)
.Tag("item_id", item.Key.ItemId.ToString())
.Tag("item_name", priceInfo.Name)
.Tag("hq", (item.Key.IsHq ? 1 : 0).ToString())
.Field("quantity", item.Sum(x => x.Quantity))
.Field("total_gil", item.Sum(x => x.Quantity) * (priceHq ? priceInfo.Hq : priceInfo.Normal));
}
}
}
private IEnumerable<PointData> GenerateFcStats(Character character, Currencies currencies, StatisticsUpdate update,
DateTime date)
{
update.FcStats.TryGetValue(character.CharacterId, out FcStats? fcStats);
Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString())
.Tag("fc_name", character.Name)
.Tag("type", character.CharacterType.ToString())
.Timestamp(date, WritePrecision.S);
yield return pointData("currency")
.Field("gil", currencies.Gil)
.Field("fccredit", fcStats?.FcCredits ?? 0)
.Field("ceruleum_tanks", currencies.CeruleumTanks)
.Field("repair_kits", currencies.RepairKits);
foreach (var inventoryPoint in GenerateInventoryStats(character.CharacterId, update, pointData))
yield return inventoryPoint;
}
public void Dispose() public void Dispose()
{ {
_influxClient?.Dispose(); _influxClient?.Dispose();
} }
private struct PriceInfo
{
public string Name { get; init; }
public uint Normal { get; init; }
public uint Hq => Normal + (uint)Math.Ceiling((decimal)Normal / 10);
public uint UiCategory { get; set; }
}
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -66,7 +67,7 @@ public class InfluxPlugin : IDalamudPlugin
_windowSystem = new WindowSystem(typeof(InfluxPlugin).FullName); _windowSystem = new WindowSystem(typeof(InfluxPlugin).FullName);
_statisticsWindow = new StatisticsWindow(); _statisticsWindow = new StatisticsWindow();
_windowSystem.AddWindow(_statisticsWindow); _windowSystem.AddWindow(_statisticsWindow);
_configurationWindow = new ConfigurationWindow(_pluginInterface, clientState, _configuration); _configurationWindow = new ConfigurationWindow(_pluginInterface, clientState, _configuration, _allaganToolsIpc);
_configurationWindow.ConfigUpdated += (_, _) => _influxStatisticsClient.UpdateClient(); _configurationWindow.ConfigUpdated += (_, _) => _influxStatisticsClient.UpdateClient();
_windowSystem.AddWindow(_configurationWindow); _windowSystem.AddWindow(_configurationWindow);
@ -110,6 +111,18 @@ public class InfluxPlugin : IDalamudPlugin
if (characters.Count == 0) if (characters.Count == 0)
return; return;
Dictionary<string, IReadOnlyList<SortingResult>> inventoryItems =
_configuration.IncludedInventoryFilters.Select(c => c.Name)
.Distinct()
.ToDictionary(c => c, c =>
{
var filter = _allaganToolsIpc.GetFilter(c);
if (filter == null)
return new List<SortingResult>();
return filter.GenerateFilteredList();
});
var update = new StatisticsUpdate var update = new StatisticsUpdate
{ {
Currencies = currencies Currencies = currencies
@ -118,6 +131,7 @@ public class InfluxPlugin : IDalamudPlugin
y.LocalContentId == x.Key.OwnerId || y.LocalContentId == x.Key.OwnerId ||
characters.Any(z => y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId))) characters.Any(z => y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId)))
.ToDictionary(x => x.Key, x => x.Value), .ToDictionary(x => x.Key, x => x.Value),
InventoryItems = inventoryItems,
Submarines = _submarineTrackerIpc.GetSubmarineStats(characters), Submarines = _submarineTrackerIpc.GetSubmarineStats(characters),
LocalStats = _localStatsCalculator.GetAllCharacterStats() LocalStats = _localStatsCalculator.GetAllCharacterStats()
.Where(x => characters.Any(y => y.CharacterId == x.Key)) .Where(x => characters.Any(y => y.CharacterId == x.Key))

View File

@ -8,6 +8,7 @@ namespace Influx;
internal sealed class StatisticsUpdate internal sealed class StatisticsUpdate
{ {
public required IReadOnlyDictionary<Character, Currencies> Currencies { get; init; } public required IReadOnlyDictionary<Character, Currencies> Currencies { get; init; }
public required IReadOnlyDictionary<string, IReadOnlyList<SortingResult>> InventoryItems { get; init; }
public required Dictionary<Character, List<SubmarineStats>> Submarines { get; init; } public required Dictionary<Character, List<SubmarineStats>> Submarines { get; init; }
public required Dictionary<Character, LocalStats> LocalStats { get; init; } public required Dictionary<Character, LocalStats> LocalStats { get; init; }
public required Dictionary<ulong, FcStats> FcStats { get; init; } public required Dictionary<ulong, FcStats> FcStats { get; init; }

View File

@ -1,11 +1,14 @@
using System; using System;
using System.Linq; using System.Linq;
using Dalamud.Interface;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using ImGuiNET; using ImGuiNET;
using Influx.AllaganTools;
namespace Influx.Windows; namespace Influx.Windows;
@ -14,14 +17,18 @@ internal sealed class ConfigurationWindow : Window
private readonly DalamudPluginInterface _pluginInterface; private readonly DalamudPluginInterface _pluginInterface;
private readonly IClientState _clientState; private readonly IClientState _clientState;
private readonly Configuration _configuration; private readonly Configuration _configuration;
private readonly AllaganToolsIpc _allaganToolsIpc;
private string[] _filterNames = Array.Empty<string>();
private int _filterIndexToAdd = 0;
public ConfigurationWindow(DalamudPluginInterface pluginInterface, IClientState clientState, public ConfigurationWindow(DalamudPluginInterface pluginInterface, IClientState clientState,
Configuration configuration) Configuration configuration, AllaganToolsIpc allaganToolsIpc)
: base("Configuration###InfluxConfiguration") : base("Configuration###InfluxConfiguration")
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
_clientState = clientState; _clientState = clientState;
_configuration = configuration; _configuration = configuration;
_allaganToolsIpc = allaganToolsIpc;
} }
public event EventHandler? ConfigUpdated; public event EventHandler? ConfigUpdated;
@ -33,9 +40,19 @@ internal sealed class ConfigurationWindow : Window
{ {
DrawConnectionSettings(); DrawConnectionSettings();
DrawIncludedCharacters(); DrawIncludedCharacters();
DrawAllaganToolsFilters();
} }
} }
public override void OnOpen()
{
_filterNames = _allaganToolsIpc.GetSearchFilters()
.Select(x => x.Value)
.Order()
.ToArray();
_filterIndexToAdd = 0;
}
private void DrawConnectionSettings() private void DrawConnectionSettings()
{ {
using var tabItem = ImRaii.TabItem("Connection Settings"); using var tabItem = ImRaii.TabItem("Connection Settings");
@ -165,6 +182,64 @@ internal sealed class ConfigurationWindow : Window
} }
} }
private void DrawAllaganToolsFilters()
{
using var tabItem = ImRaii.TabItem("Inventory Filters");
if (!tabItem)
return;
if (_configuration.IncludedInventoryFilters.Count > 0)
{
int? indexToRemove = null;
ImGui.Text("Selected Filters:");
ImGui.Indent(30);
foreach (var filter in _configuration.IncludedInventoryFilters)
{
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, $"{filter.Name}"))
{
indexToRemove = _configuration.IncludedInventoryFilters.IndexOf(filter);
}
}
ImGui.Unindent(30);
if (indexToRemove != null)
{
_configuration.IncludedInventoryFilters.RemoveAt(indexToRemove.Value);
Save();
}
}
else
{
ImGui.Text("You are not tracking any AllaganTools filters.");
}
ImGui.Separator();
if (_filterNames.Length > 0)
{
ImGui.Combo("Add Search Filter", ref _filterIndexToAdd, _filterNames, _filterNames.Length);
ImGui.BeginDisabled(_configuration.IncludedInventoryFilters.Any(x => x.Name == _filterNames[_filterIndexToAdd]));
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Track Filter"))
{
_configuration.IncludedInventoryFilters.Add(new Configuration.FilterInfo
{
Name = _filterNames[_filterIndexToAdd],
});
Save();
}
ImGui.EndDisabled();
}
else
{
ImGui.TextColored(ImGuiColors.DalamudRed,
"You don't have any search filters, or the AllaganTools integration doesn't work.");
}
}
private void Save(bool sendEvent = false) private void Save(bool sendEvent = false)
{ {
_pluginInterface.SavePluginConfig(_configuration); _pluginInterface.SavePluginConfig(_configuration);