Track AllaganTools filters
This commit is contained in:
parent
c7ceaa2cfd
commit
ef1769c4f7
@ -15,22 +15,27 @@ internal sealed class AllaganToolsIpc : IDisposable
|
||||
private readonly DalamudReflector _dalamudReflector;
|
||||
private readonly IFramework _framework;
|
||||
private readonly IPluginLog _pluginLog;
|
||||
private readonly ICallGateSubscriber<bool, bool>? _initalized;
|
||||
private readonly ICallGateSubscriber<bool, bool>? _initialized;
|
||||
private readonly ICallGateSubscriber<bool>? _isInitialized;
|
||||
private readonly ICallGateSubscriber<Dictionary<string, string>> _getSearchFilters;
|
||||
|
||||
public ICharacterMonitor Characters { get; private set; } = new UnavailableCharacterMonitor();
|
||||
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;
|
||||
_dalamudReflector = dalamudReflector;
|
||||
_framework = framework;
|
||||
_pluginLog = pluginLog;
|
||||
|
||||
_initalized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
|
||||
_initialized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
|
||||
_isInitialized = pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
|
||||
_initalized.Subscribe(ConfigureIpc);
|
||||
_initialized.Subscribe(ConfigureIpc);
|
||||
_getSearchFilters =
|
||||
pluginInterface.GetIpcSubscriber<Dictionary<string, string>>("AllaganTools.GetSearchFilters");
|
||||
|
||||
try
|
||||
{
|
||||
@ -58,6 +63,7 @@ internal sealed class AllaganToolsIpc : IDisposable
|
||||
Characters = new CharacterMonitor(pluginService.GetProperty("CharacterMonitor")!.GetValue(null)!);
|
||||
Inventories = new InventoryMonitor(
|
||||
pluginService.GetProperty("InventoryMonitor")!.GetValue(null)!);
|
||||
Filters = new FilterService(pluginService.GetProperty("FilterService")!.GetValue(null)!);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -72,6 +78,32 @@ internal sealed class AllaganToolsIpc : IDisposable
|
||||
}, 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()
|
||||
{
|
||||
_pluginLog.Debug($"{Characters.GetType()}, {Inventories.GetType()}");
|
||||
@ -98,9 +130,10 @@ internal sealed class AllaganToolsIpc : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_initalized?.Unsubscribe(ConfigureIpc);
|
||||
_initialized?.Unsubscribe(ConfigureIpc);
|
||||
Characters = new UnavailableCharacterMonitor();
|
||||
Inventories = new UnavailableInventoryMonitor();
|
||||
Filters = new UnavailableFilterService();
|
||||
}
|
||||
|
||||
private sealed class InventoryWrapper
|
||||
|
29
Influx/AllaganTools/Filter.cs
Normal file
29
Influx/AllaganTools/Filter.cs
Normal 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();
|
||||
}
|
||||
}
|
21
Influx/AllaganTools/FilterService.cs
Normal file
21
Influx/AllaganTools/FilterService.cs
Normal 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;
|
||||
}
|
||||
}
|
6
Influx/AllaganTools/IFilterService.cs
Normal file
6
Influx/AllaganTools/IFilterService.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Influx.AllaganTools;
|
||||
|
||||
internal interface IFilterService
|
||||
{
|
||||
Filter? GetFilterByKeyOrName(string keyOrName);
|
||||
}
|
27
Influx/AllaganTools/SortingResult.cs
Normal file
27
Influx/AllaganTools/SortingResult.cs
Normal 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}";
|
||||
}
|
||||
}
|
6
Influx/AllaganTools/UnavailableFilterService.cs
Normal file
6
Influx/AllaganTools/UnavailableFilterService.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Influx.AllaganTools;
|
||||
|
||||
internal sealed class UnavailableFilterService : IFilterService
|
||||
{
|
||||
public Filter? GetFilterByKeyOrName(string keyOrName) => null;
|
||||
}
|
@ -10,6 +10,7 @@ public sealed class Configuration : IPluginConfiguration
|
||||
public ServerConfiguration Server { get; set; } = new();
|
||||
|
||||
public List<CharacterInfo> IncludedCharacters { get; set; } = new();
|
||||
public List<FilterInfo> IncludedInventoryFilters { get; set; } = new();
|
||||
|
||||
public sealed class ServerConfiguration
|
||||
{
|
||||
@ -27,4 +28,9 @@ public sealed class Configuration : IPluginConfiguration
|
||||
public string? CachedWorldName { get; set; }
|
||||
public bool IncludeFreeCompany { get; set; } = true;
|
||||
}
|
||||
|
||||
public sealed class FilterInfo
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<Version>0.8</Version>
|
||||
<Version>0.9</Version>
|
||||
<LangVersion>11.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -7,6 +7,7 @@ using Influx.AllaganTools;
|
||||
using Influx.LocalStatistics;
|
||||
using InfluxDB.Client;
|
||||
using InfluxDB.Client.Api.Domain;
|
||||
using InfluxDB.Client.Core;
|
||||
using InfluxDB.Client.Writes;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
||||
@ -22,7 +23,8 @@ internal sealed class InfluxStatisticsClient : IDisposable
|
||||
private readonly IPluginLog _pluginLog;
|
||||
private readonly IReadOnlyDictionary<byte, byte> _classJobToArrayIndex;
|
||||
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,
|
||||
IClientState clientState, IPluginLog pluginLog)
|
||||
@ -41,6 +43,13 @@ internal sealed class InfluxStatisticsClient : IDisposable
|
||||
.Where(x => x.JobIndex > 0)
|
||||
.Where(x => x.Abbreviation.ToString() != "SMN")
|
||||
.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 &&
|
||||
@ -84,143 +93,16 @@ internal sealed class InfluxStatisticsClient : IDisposable
|
||||
{
|
||||
if (character.CharacterType == CharacterType.Character)
|
||||
{
|
||||
update.LocalStats.TryGetValue(character, out LocalStats? localStats);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
values.AddRange(GenerateCharacterStats(character, currencies, update, date));
|
||||
}
|
||||
else if (character.CharacterType == CharacterType.Retainer)
|
||||
{
|
||||
var owner = currencyStats.Keys.First(x => x.CharacterId == character.OwnerId);
|
||||
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));
|
||||
}
|
||||
values.AddRange(GenerateRetainerStats(character, currencies, update, date));
|
||||
}
|
||||
else if (character.CharacterType == CharacterType.FreeCompanyChest &&
|
||||
validFcIds.Contains(character.CharacterId))
|
||||
{
|
||||
update.FcStats.TryGetValue(character.CharacterId, out FcStats? fcStats);
|
||||
|
||||
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));
|
||||
values.AddRange(GenerateFcStats(character, currencies, update, date));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
{
|
||||
_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; }
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -66,7 +67,7 @@ public class InfluxPlugin : IDalamudPlugin
|
||||
_windowSystem = new WindowSystem(typeof(InfluxPlugin).FullName);
|
||||
_statisticsWindow = new StatisticsWindow();
|
||||
_windowSystem.AddWindow(_statisticsWindow);
|
||||
_configurationWindow = new ConfigurationWindow(_pluginInterface, clientState, _configuration);
|
||||
_configurationWindow = new ConfigurationWindow(_pluginInterface, clientState, _configuration, _allaganToolsIpc);
|
||||
_configurationWindow.ConfigUpdated += (_, _) => _influxStatisticsClient.UpdateClient();
|
||||
_windowSystem.AddWindow(_configurationWindow);
|
||||
|
||||
@ -110,6 +111,18 @@ public class InfluxPlugin : IDalamudPlugin
|
||||
if (characters.Count == 0)
|
||||
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
|
||||
{
|
||||
Currencies = currencies
|
||||
@ -118,6 +131,7 @@ public class InfluxPlugin : IDalamudPlugin
|
||||
y.LocalContentId == x.Key.OwnerId ||
|
||||
characters.Any(z => y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId)))
|
||||
.ToDictionary(x => x.Key, x => x.Value),
|
||||
InventoryItems = inventoryItems,
|
||||
Submarines = _submarineTrackerIpc.GetSubmarineStats(characters),
|
||||
LocalStats = _localStatsCalculator.GetAllCharacterStats()
|
||||
.Where(x => characters.Any(y => y.CharacterId == x.Key))
|
||||
|
@ -8,6 +8,7 @@ namespace Influx;
|
||||
internal sealed class StatisticsUpdate
|
||||
{
|
||||
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, LocalStats> LocalStats { get; init; }
|
||||
public required Dictionary<ulong, FcStats> FcStats { get; init; }
|
||||
|
@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Services;
|
||||
using ImGuiNET;
|
||||
using Influx.AllaganTools;
|
||||
|
||||
namespace Influx.Windows;
|
||||
|
||||
@ -14,14 +17,18 @@ internal sealed class ConfigurationWindow : Window
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly IClientState _clientState;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly AllaganToolsIpc _allaganToolsIpc;
|
||||
private string[] _filterNames = Array.Empty<string>();
|
||||
private int _filterIndexToAdd = 0;
|
||||
|
||||
public ConfigurationWindow(DalamudPluginInterface pluginInterface, IClientState clientState,
|
||||
Configuration configuration)
|
||||
Configuration configuration, AllaganToolsIpc allaganToolsIpc)
|
||||
: base("Configuration###InfluxConfiguration")
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
_clientState = clientState;
|
||||
_configuration = configuration;
|
||||
_allaganToolsIpc = allaganToolsIpc;
|
||||
}
|
||||
|
||||
public event EventHandler? ConfigUpdated;
|
||||
@ -33,9 +40,19 @@ internal sealed class ConfigurationWindow : Window
|
||||
{
|
||||
DrawConnectionSettings();
|
||||
DrawIncludedCharacters();
|
||||
DrawAllaganToolsFilters();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOpen()
|
||||
{
|
||||
_filterNames = _allaganToolsIpc.GetSearchFilters()
|
||||
.Select(x => x.Value)
|
||||
.Order()
|
||||
.ToArray();
|
||||
_filterIndexToAdd = 0;
|
||||
}
|
||||
|
||||
private void DrawConnectionSettings()
|
||||
{
|
||||
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)
|
||||
{
|
||||
_pluginInterface.SavePluginConfig(_configuration);
|
||||
|
Loading…
Reference in New Issue
Block a user