diff --git a/Plugin/Extensions/ItemExtensions.cs b/Plugin/Extensions/ItemExtensions.cs index 28f0f97..f0b8cce 100644 --- a/Plugin/Extensions/ItemExtensions.cs +++ b/Plugin/Extensions/ItemExtensions.cs @@ -16,8 +16,23 @@ namespace LootValueEX.Extensions /// The item to check. /// True if the item has been examined, false otherwise. internal static bool IsExamined(this Item? item) => item != null && ClientAppUtils.GetMainApp().GetClientBackEndSession().Profile.Examined(item); + internal static bool IsStacked(this Item? item) => item != null && (item.StackObjectsCount > 1 || item.UnlimitedCount); internal static string PrehashTemplate(this Item? item) => string.Format("{0}|{1}|{2}", item?.TemplateId, item.GetDurability(), item.GetUses()); + internal static Item? UnstackItem(this Item? item) + { + if (item == null) + return null; + + if (!item.IsStacked()) + return item; + + Item? itemClone = item.CloneItem(); + itemClone.StackObjectsCount = 1; + itemClone.UnlimitedCount = false; + return itemClone; + } + /// /// Retrieves the value of a specific attribute from an item. /// diff --git a/Plugin/Patches/Screens/InventoryScreenPatch.cs b/Plugin/Patches/Screens/InventoryScreenPatch.cs index e25eea4..7ed2a93 100644 --- a/Plugin/Patches/Screens/InventoryScreenPatch.cs +++ b/Plugin/Patches/Screens/InventoryScreenPatch.cs @@ -25,7 +25,8 @@ namespace LootValueEX.Patches.Screens Stopwatch sw = Stopwatch.StartNew(); foreach(EFT.InventoryLogic.Item item in profile.Inventory.GetPlayerItems(EFT.InventoryLogic.EPlayerItems.Equipment)) { - Plugin.Log.LogDebug($"Equip Process: {item.LocalizedName()} ({item.TemplateId}): {await item.GetCustomHashAsync()}"); + //Plugin.Log.LogDebug($"Equip Process: {item.LocalizedName()} ({item.TemplateId}): {await item.GetCustomHashAsync()}"); + continue; } sw.Stop(); Plugin.Log.LogDebug($"Equipment processing finished in {sw.ElapsedMilliseconds}ms"); @@ -43,7 +44,8 @@ namespace LootValueEX.Patches.Screens Stopwatch sw = Stopwatch.StartNew(); foreach (EFT.InventoryLogic.Item item in lootItemClass.GetAllItems()) { - Plugin.Log.LogDebug($"LootItemClass process: {item.LocalizedName()} ({item.TemplateId}): {await item.GetCustomHashAsync()}"); + //Plugin.Log.LogDebug($"LootItemClass process: {item.LocalizedName()} ({item.TemplateId}): {await item.GetCustomHashAsync()}"); + continue; } Plugin.Log.LogDebug($"LootItemClass processing finished in {sw.ElapsedMilliseconds}ms"); tcsLoot.SetResult(true); diff --git a/Plugin/Patches/Tooltips/TooltipPatch.cs b/Plugin/Patches/Tooltips/TooltipPatch.cs index 66e5968..1e48904 100644 --- a/Plugin/Patches/Tooltips/TooltipPatch.cs +++ b/Plugin/Patches/Tooltips/TooltipPatch.cs @@ -19,12 +19,10 @@ namespace LootValueEX.Patches.Tooltips [PatchPostfix] public static void AlterText(SimpleTooltip __instance, string text) { - StackTrace stackTrace = new StackTrace(); - Plugin.Log.LogDebug("Stacktrace of tooltip call: \n" + stackTrace.ToString()); if (GridItemTooltipPatch.PatchTooltip) { - text += $"
TemplateID: {GridItemTooltipPatch.HoveredItem?.TemplateId}
Template: {GridItemTooltipPatch.HoveredItem?.Template}
Item hashsum: {GridItemTooltipPatch.HoveredItem?.GetHashSum()}
Custom hash: {GridItemTooltipPatch.HoveredItem?.GetCustomHash()}
Item durability: {GridItemTooltipPatch.HoveredItem?.GetDurability()}
Item uses: {GridItemTooltipPatch.HoveredItem?.GetUses()}
GridItemView"; - Plugin.Log.LogDebug(GridItemTooltipPatch.HoveredItem?.AttributesToString()); + Structs.TradeOfferStruct tradeOffer = Utils.ItemUtils.GetBestTraderValue(GridItemTooltipPatch.HoveredItem); + text += $"
Hash: {GridItemTooltipPatch.HoveredItem?.GetCustomHash()}
Trader: {tradeOffer.TraderID}
Value: {tradeOffer.Price}
Currency: {tradeOffer.CurrencyID}
Price in rubles: {tradeOffer.PriceInRouble}
GridItemView"; } if (InsuranceSlotPatch.PatchTooltip) { diff --git a/Plugin/Patches/TraderClassPatch.cs b/Plugin/Patches/TraderClassPatch.cs new file mode 100644 index 0000000..1dc2471 --- /dev/null +++ b/Plugin/Patches/TraderClassPatch.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using SPT.Reflection.Patching; + +namespace LootValueEX.Patches +{ + internal class TraderClassPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() => typeof(TraderClass).GetConstructors().First(); + + [PatchPostfix] + private static void GenerateSupplyData(ref TraderClass __instance) + { + Plugin.Log.LogDebug($"Generating Assortment Data for trader {__instance.Id}"); + __instance.RefreshAssortment(true, false); + } + } +} diff --git a/Plugin/Plugin.cs b/Plugin/Plugin.cs index aaffaeb..84770b1 100644 --- a/Plugin/Plugin.cs +++ b/Plugin/Plugin.cs @@ -11,6 +11,8 @@ namespace LootValueEX { Log = base.Logger; + new Patches.TraderClassPatch().Enable(); + new Patches.Tooltips.GridItemTooltipPatch().Enable(); new Patches.Tooltips.TooltipPatch().Enable(); new Patches.Tooltips.InsuranceGridPatch().Enable(); diff --git a/Plugin/Structs/TradeOfferStruct.cs b/Plugin/Structs/TradeOfferStruct.cs new file mode 100644 index 0000000..52b7091 --- /dev/null +++ b/Plugin/Structs/TradeOfferStruct.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LootValueEX.Structs +{ + internal readonly struct TradeOfferStruct + { + internal readonly string TraderID; + internal readonly string CurrencyID; + internal readonly int Price; + internal readonly float PriceInRouble; + internal TradeOfferStruct(string traderId, string currencyId, int price, float priceInRouble) + { + TraderID = traderId; + CurrencyID = currencyId; + Price = price; + PriceInRouble = priceInRouble; + } + } +} diff --git a/Plugin/Utils/EconomyUtils.cs b/Plugin/Utils/EconomyUtils.cs new file mode 100644 index 0000000..6a02515 --- /dev/null +++ b/Plugin/Utils/EconomyUtils.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Comfort.Common; +using EFT.InventoryLogic; +using SPT.Reflection.Utils; +using EFTCurrencyUtils = GClass2531; + +namespace LootValueEX.Utils +{ + internal class EconomyUtils + { + + public static float ConvertToRuble(string id, float amount) + { + if (!EFTCurrencyUtils.TryGetCurrencyType(id, out ECurrencyType currencyType)) + return 0f; + if (currencyType.Equals(ECurrencyType.RUB)) + return amount; + return amount * (float)Singleton.Instance.GetBasePrice(id); + } + + public static Structs.TradeOfferStruct GetTraderItemOffer(TraderClass trader, Item item) + { + Plugin.Log.LogDebug($"GetTraderItemOffer: {item.LocalizedName()} - {trader.LocalizedName}"); + TraderClass.GStruct244? tradeOffer = trader.GetUserItemPrice(item); + + if (tradeOffer == null) + { + Plugin.Log.LogDebug("GetTraderItemOffer: tradeOffer == null"); + return default; + } + if(tradeOffer.Value.Amount > 0) + { + Plugin.Log.LogDebug($"{trader.LocalizedName}\n\tCurrencyID: {tradeOffer.Value.CurrencyId}\n\tValue: {tradeOffer.Value.Amount}\n\tPrice in RB: {EconomyUtils.ConvertToRuble(tradeOffer.Value.CurrencyId, tradeOffer.Value.Amount)}"); + return new Structs.TradeOfferStruct(trader.Id, tradeOffer.Value.CurrencyId, tradeOffer.Value.Amount, EconomyUtils.ConvertToRuble(tradeOffer.Value.CurrencyId, tradeOffer.Value.Amount)); + } + Plugin.Log.LogDebug("GetTraderItemOffer: no value"); + return default; + } + } +} diff --git a/Plugin/Utils/ItemUtils.cs b/Plugin/Utils/ItemUtils.cs index 012ff5a..bb2daa2 100644 --- a/Plugin/Utils/ItemUtils.cs +++ b/Plugin/Utils/ItemUtils.cs @@ -1,4 +1,6 @@ using EFT.InventoryLogic; +using LootValueEX.Extensions; +using SPT.Reflection.Utils; namespace LootValueEX.Utils { @@ -13,5 +15,23 @@ namespace LootValueEX.Utils } return totalDurability; } + + public static Task GetBestTraderValueAsync(Item item) + { + Structs.TradeOfferStruct bestOffer = new(); + item = item.UnstackItem(); + foreach (TraderClass trader in ClientAppUtils.GetMainApp().GetClientBackEndSession().DisplayableTraders) + { + Structs.TradeOfferStruct currentOffer = EconomyUtils.GetTraderItemOffer(trader, item); + + if (currentOffer.Equals(default) || currentOffer.Price <= 0) + continue; + + if (currentOffer.PriceInRouble > bestOffer.PriceInRouble) + bestOffer = currentOffer; + } + return Task.FromResult(bestOffer); + } + public static Structs.TradeOfferStruct GetBestTraderValue(Item item) => Task.Run(() => GetBestTraderValueAsync(item)).Result; } }