diff --git a/AetherBags/Inventory/CurrencyInfo.cs b/AetherBags/Inventory/CurrencyInfo.cs index 2a2c76e..a26b8d0 100644 --- a/AetherBags/Inventory/CurrencyInfo.cs +++ b/AetherBags/Inventory/CurrencyInfo.cs @@ -2,7 +2,10 @@ namespace AetherBags.Currency; public class CurrencyInfo { - public required int Amount { get; set; } + public required uint Amount { get; set; } + public required uint MaxAmount { get; set; } public required uint ItemId { get; set; } public required uint IconId { get; set; } + public required bool LimitReached { get; set; } + public required bool IsCapped { get; set; } } \ No newline at end of file diff --git a/AetherBags/Inventory/InventoryState.cs b/AetherBags/Inventory/InventoryState.cs index 575a9ef..553a0e3 100644 --- a/AetherBags/Inventory/InventoryState.cs +++ b/AetherBags/Inventory/InventoryState.cs @@ -250,13 +250,81 @@ public static unsafe class InventoryState return $"{used}/140"; } - public static CurrencyInfo GetCurrencyInfo(uint itemId) + private const uint CurrencyIdLimitedTomestone = 0xFFFF_FFFE; + private const uint CurrencyIdNonLimitedTomestone = 0xFFFF_FFFD; + + private static uint? GetLimitedTomestoneItemId() + => Services.DataManager.GetExcelSheet() + .FirstOrDefault(t => t.Tomestones.RowId == 3) + .Item.RowId; + + private static uint? GetNonLimitedTomestoneItemId() + => Services.DataManager.GetExcelSheet() + .FirstOrDefault(t => t.Tomestones.RowId == 2) + .Item.RowId; + + private static CurrencyItem ResolveCurrencyItemId(uint currencyId) { + uint itemId = currencyId; + bool isLimited = false; + + if (currencyId == CurrencyIdLimitedTomestone) + { + itemId = GetLimitedTomestoneItemId() ?? 0; + isLimited = true; + } + + if (currencyId == CurrencyIdNonLimitedTomestone) + { + itemId = GetNonLimitedTomestoneItemId() ?? 0; + } + + return new CurrencyItem(itemId, isLimited); + } + + + public static IReadOnlyList GetCurrencyInfoList(uint[] currencyIds) + { + if (currencyIds.Length == 0) return Array.Empty(); + + List currencyInfoList = new List(currencyIds.Length); + + for (int i = 0; i < currencyIds.Length; i++) + { + CurrencyItem currencyItem = ResolveCurrencyItemId(currencyIds[i]); + if (currencyItem.ItemId == 0) + continue; + + currencyInfoList.Add(GetCurrencyInfo(currencyItem)); + } + + return currencyInfoList; + } + + private static CurrencyInfo GetCurrencyInfo(CurrencyItem currencyItem) + { + InventoryManager* inventoryManager = InventoryManager.Instance(); + var item = Services.DataManager.GetExcelSheet().GetRow(currencyItem.ItemId); + + uint amount = (uint) inventoryManager->GetInventoryItemCount(currencyItem.ItemId); + uint maxAmount = item.StackSize; + bool isCapped = false; + if (currencyItem.IsLimited) + { + int weeklyLimit = InventoryManager.GetLimitedTomestoneWeeklyLimit(); + int weeklyAcquired = inventoryManager->GetWeeklyAcquiredTomestoneCount(); + isCapped = weeklyAcquired >= weeklyLimit; + } + + Services.Logger.Info($"Currency {currencyItem.ItemId} amount: {amount}, max: {maxAmount}"); return new CurrencyInfo { - Amount = InventoryManager.Instance()->GetInventoryItemCount(1), - ItemId = itemId, - IconId = Services.DataManager.GetExcelSheet().GetRow(itemId).Icon + Amount = amount, + MaxAmount = item.StackSize, + ItemId = currencyItem.ItemId, + IconId = item.Icon, + LimitReached = amount >= maxAmount, + IsCapped = isCapped }; } @@ -339,4 +407,6 @@ public static unsafe class InventoryState public List FilteredItems = null!; public bool Used; } + + private record CurrencyItem(uint ItemId, bool IsLimited); } diff --git a/AetherBags/Nodes/CurrencyListNode.cs b/AetherBags/Nodes/CurrencyListNode.cs new file mode 100644 index 0000000..f465a74 --- /dev/null +++ b/AetherBags/Nodes/CurrencyListNode.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using AetherBags.Currency; +using KamiToolKit.Nodes; + +namespace AetherBags.Nodes; + +public class CurrencyListNode : HorizontalListNode +{ + public List CurrencyInfoList + { + get; + set; + } +} \ No newline at end of file diff --git a/AetherBags/Nodes/CurrencyNode.cs b/AetherBags/Nodes/CurrencyNode.cs index 94ad96d..dbaa6d0 100644 --- a/AetherBags/Nodes/CurrencyNode.cs +++ b/AetherBags/Nodes/CurrencyNode.cs @@ -9,39 +9,45 @@ namespace AetherBags.Nodes; public class CurrencyNode : SimpleComponentNode { - private IconImageNode iconImageNode; - private TextNode countNode; + private readonly IconImageNode _iconImageNode; + private readonly TextNode _countNode; public CurrencyNode() { - iconImageNode = new IconImageNode + _iconImageNode = new IconImageNode { - FitTexture = true + FitTexture = true, + Size = new Vector2(24f) }; - iconImageNode.AttachNode(this); + _iconImageNode.AttachNode(this); - - countNode = new TextNode + _countNode = new TextNode { TextFlags = TextFlags.Emboss, TextColor = ColorHelper.GetColor(8), TextOutlineColor = ColorHelper.GetColor(7), + AlignmentType = AlignmentType.Left, FontSize = 14, + Size = new Vector2(120.0f, 28.0f) }; - countNode.AttachNode(this); + _countNode.AttachNode(this); } public required CurrencyInfo Currency { get; set { field = value; - iconImageNode.IconId = value.IconId; - countNode.String = value.Amount.ToString("N0"); + _iconImageNode.IconId = value.IconId; + _iconImageNode.Position = new Vector2(0f, 2f); - countNode.Size = new Vector2(120.0f, 28.0f); - countNode.Origin = countNode.Size / 2.0f; - countNode.Position = new Vector2(26.0f, 0.0f); - iconImageNode.Size = new Vector2(24f); + _countNode.String = value.Amount.ToString("N0"); + _countNode.Position = new Vector2(_iconImageNode.Bounds.Right + 2f, 0f); + + // Limit > Capped > Normal + _countNode.TextColor = + value.LimitReached ? ColorHelper.GetColor(17) : + value.IsCapped ? ColorHelper.GetColor(43) : + ColorHelper.GetColor(8); } } } \ No newline at end of file diff --git a/AetherBags/Nodes/InventoryFooterNode.cs b/AetherBags/Nodes/InventoryFooterNode.cs index eaaf7ec..3f936a3 100644 --- a/AetherBags/Nodes/InventoryFooterNode.cs +++ b/AetherBags/Nodes/InventoryFooterNode.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Numerics; using AetherBags.Currency; using AetherBags.Inventory; @@ -12,7 +14,7 @@ namespace AetherBags.Nodes; public sealed class InventoryFooterNode : SimpleComponentNode { private readonly TextNode _slotAmountTextNode; - private readonly CurrencyNode _currencyNode; + private readonly CurrencyListNode _currencyListNode; public InventoryFooterNode() { @@ -28,12 +30,32 @@ public sealed class InventoryFooterNode : SimpleComponentNode }; _slotAmountTextNode.AttachNode(this); - _currencyNode = new CurrencyNode + _currencyListNode = new CurrencyListNode { + Position = new Vector2(0, 0), Size = new Vector2(120, 28), - Currency = InventoryState.GetCurrencyInfo(1) }; - _currencyNode.AttachNode(this); + _currencyListNode.AttachNode(this); + + RefreshCurrencies(); + } + + public void RefreshCurrencies() + { + IReadOnlyList currencyInfoList = InventoryState.GetCurrencyInfoList([1, 28, 0xFFFF_FFFE, 0xFFFF_FFFD]); + _currencyListNode.SyncWithListDataByKey( + dataList: currencyInfoList, + getKeyFromData: c => c.ItemId, + getKeyFromNode: n => n.Currency.ItemId, + updateNode: (node, data) => + { + node.Currency = data; + }, + createNodeMethod: data => new CurrencyNode + { + Size = new Vector2(120, 28), + Currency = data + }); } public string SlotAmountText @@ -46,6 +68,5 @@ public sealed class InventoryFooterNode : SimpleComponentNode base.OnSizeChanged(); _slotAmountTextNode.Position = new Vector2(Size.X - _slotAmountTextNode.Size.X - 10, 0); - _currencyNode.Position = new Vector2(0, 0); } } \ No newline at end of file diff --git a/KamiToolKit b/KamiToolKit index aa27815..9101fc8 160000 --- a/KamiToolKit +++ b/KamiToolKit @@ -1 +1 @@ -Subproject commit aa278153f335a368971b7ae90041d29de14f3289 +Subproject commit 9101fc8a8e861345fe0740e7791cdb06e8764f3e