Compare commits
8 Commits
ee5bbbb098
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
7919daa086
|
|||
|
cabd008204
|
|||
|
7734090feb
|
|||
|
db4003caa7
|
|||
|
a86213d3a6
|
|||
|
fa0821686b
|
|||
|
ac5140ce24
|
|||
|
313763cfc4
|
72
Plugin/Common/TaskCache.cs
Normal file
72
Plugin/Common/TaskCache.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace LootValueEX.Common;
|
||||||
|
|
||||||
|
internal class TaskCache
|
||||||
|
{
|
||||||
|
private readonly SemaphoreSlim _semaphore;
|
||||||
|
private readonly ConcurrentDictionary<Guid, Task> _taskDict;
|
||||||
|
|
||||||
|
internal TaskCache(int maxTasks = 10)
|
||||||
|
{
|
||||||
|
_semaphore = new SemaphoreSlim(maxTasks);
|
||||||
|
_taskDict = new ConcurrentDictionary<Guid, Task>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsTaskCancelled(Guid taskGuid) =>
|
||||||
|
_taskDict.TryGetValue(taskGuid, out Task task) && task.Status == TaskStatus.Canceled;
|
||||||
|
|
||||||
|
internal Guid AddTask(Func<CancellationToken, Task> taskFactory)
|
||||||
|
{
|
||||||
|
Guid taskGuid = Guid.NewGuid();
|
||||||
|
CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
|
Task task = taskFactory(cts.Token);
|
||||||
|
|
||||||
|
_taskDict[taskGuid] = task;
|
||||||
|
|
||||||
|
return taskGuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool RemoveTask(Guid taskGuid)
|
||||||
|
{
|
||||||
|
if (_taskDict.TryGetValue(taskGuid, out Task task))
|
||||||
|
{
|
||||||
|
if (_taskDict.TryRemove(taskGuid, out _))
|
||||||
|
{
|
||||||
|
task.Dispose();
|
||||||
|
_semaphore.Release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<Task> RunTaskByGuidAsync(Guid taskGuid)
|
||||||
|
{
|
||||||
|
await _semaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ((_taskDict.TryGetValue(taskGuid, out Task task)))
|
||||||
|
{
|
||||||
|
if (!task.IsCompleted && !task.Status.Equals(TaskStatus.Running))
|
||||||
|
await task;
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
Plugin.Log.LogError($"Task {taskGuid.ToString()} not found.");
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
RemoveTask(taskGuid);
|
||||||
|
Plugin.Log.LogDebug($"Task {taskGuid.ToString()} got cancelled.");
|
||||||
|
}
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<Task> AddAndRunTaskAsync(Func<CancellationToken, Task> taskFactory)
|
||||||
|
{
|
||||||
|
Guid taskGuid = AddTask(taskFactory);
|
||||||
|
return await RunTaskByGuidAsync(taskGuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,41 @@ namespace LootValueEX.Extensions
|
|||||||
{
|
{
|
||||||
internal static class ItemExtensions
|
internal static class ItemExtensions
|
||||||
{
|
{
|
||||||
internal static bool IsExamined(this Item? item) => item != null && ClientAppUtils.GetMainApp().GetClientBackEndSession().Profile.Examined(item);
|
/// <summary>
|
||||||
|
/// Checks if an item has been examined by the player.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to check.</param>
|
||||||
|
/// <returns>True if the item has been examined, false otherwise.</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the value of a specific attribute from an item.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to retrieve the attribute from.</param>
|
||||||
|
/// <param name="attributeId">The ID of the attribute to retrieve.</param>
|
||||||
|
/// <returns>The value of the attribute, or -1f if the item is null or the attribute is not found.</returns>
|
||||||
internal static float GetItemAttribute(this Item? item, EItemAttributeId attributeId)
|
internal static float GetItemAttribute(this Item? item, EItemAttributeId attributeId)
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
@@ -24,50 +58,89 @@ namespace LootValueEX.Extensions
|
|||||||
return -1f;
|
return -1f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal static string GetCustomHash(this Item? item)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a custom hash for the given item.
|
||||||
|
///
|
||||||
|
/// This function takes an optional Item as a parameter and returns its custom hash.
|
||||||
|
/// If the item is null, it returns an empty string. The custom hash is determined by the item's template, durability, and uses.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to retrieve the custom hash for.</param>
|
||||||
|
/// <returns>The custom hash of the item, or an empty string if the item is null.</returns>
|
||||||
|
internal static async Task<string> GetCustomHashAsync(this Item? item)
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
StringBuilder prehashString = new StringBuilder();
|
StringBuilder prehashString = new StringBuilder();
|
||||||
item.GetAllItems().Where(prop => !prop.Equals(item)).ExecuteForEach(prop => prehashString.Append(prop.GetCustomHash()));
|
|
||||||
if (item.Template.Equals(typeof(MagazineTemplate))){
|
item.GetAllItems().Where(i => !i.Equals(item)).DoMap(i => prehashString.Append(i.PrehashTemplate()));
|
||||||
|
if (item.Template.Equals(typeof(MagazineTemplate)))
|
||||||
|
{
|
||||||
MagazineTemplate magTemplate = (MagazineTemplate)item.Template;
|
MagazineTemplate magTemplate = (MagazineTemplate)item.Template;
|
||||||
magTemplate.Cartridges.ExecuteForEach(prop => prop.Items.ExecuteForEach(ammo => prehashString.Append(ammo.GetCustomHash())));
|
magTemplate.Cartridges.DoMap(s => s.Items.DoMap(i => prehashString.Append(i.PrehashTemplate())));
|
||||||
}
|
}
|
||||||
string itemHashTemplate = string.Format("{0}|{1}|{2}|{3}", prehashString.ToString(), item.TemplateId, item.GetDurability(), item.GetUses());
|
|
||||||
return Utils.HashingUtils.ConvertToSha256(itemHashTemplate);
|
prehashString.Append(item.PrehashTemplate());
|
||||||
|
return await Task.Run(() => Utils.HashingUtils.ConvertToSha256(prehashString.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static string GetCustomHash(this Item? item) =>
|
||||||
|
Task.Run(() => item?.GetCustomHashAsync()).Result ?? string.Empty;
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
internal static string AttributesToString(this Item? item)
|
internal static string AttributesToString(this Item? item)
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
return String.Empty;
|
return String.Empty;
|
||||||
|
|
||||||
if (item.Attributes.IsNullOrEmpty())
|
if (item.Attributes.IsNullOrEmpty())
|
||||||
return String.Empty;
|
return String.Empty;
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
item.Attributes.ForEach(attr => sb.Append($"\n{attr.Id}\n\tName: {attr.Name}\n\tBase Value: {attr.Base()}\n\tString value: {attr.StringValue()}\n\tDisplay Type: {attr.DisplayType()}"));
|
item.Attributes.ForEach(attr =>
|
||||||
|
sb.Append(
|
||||||
|
$"\n{attr.Id}\n\tName: {attr.Name}\n\tBase Value: {attr.Base()}\n\tString value: {attr.StringValue()}\n\tDisplay Type: {attr.DisplayType()}"));
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the durability of a given item.
|
||||||
|
///
|
||||||
|
/// This function takes an optional Item as a parameter and returns its durability.
|
||||||
|
/// If the item is null, it returns -1f. The durability is determined by the item's template.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to retrieve the durability for.</param>
|
||||||
|
/// <returns>The durability of the item, or -1f if the item is null.</returns>
|
||||||
internal static float GetDurability(this Item? item)
|
internal static float GetDurability(this Item? item)
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
return -1f;
|
return -1f;
|
||||||
|
|
||||||
switch (item.Template)
|
switch (item.Template)
|
||||||
{
|
{
|
||||||
case ArmoredRigTemplate armoredRig:
|
case ArmoredRigTemplate armoredRig:
|
||||||
case ArmorTemplate armor:
|
case ArmorTemplate armor:
|
||||||
return Utils.ItemUtils.GetArmorDurability(item.GetItemComponentsInChildren<RepairableComponent>(true));
|
return Utils.ItemUtils.GetArmorDurability(
|
||||||
|
item.GetItemComponentsInChildren<RepairableComponent>(true));
|
||||||
default:
|
default:
|
||||||
return item.GetItemAttribute(EItemAttributeId.Durability);
|
return item.GetItemAttribute(EItemAttributeId.Durability);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the number of uses remaining for a given item.
|
||||||
|
///
|
||||||
|
/// This function takes an optional Item as a parameter and returns the number of uses remaining.
|
||||||
|
/// If the item is null, it returns -1f. The number of uses is determined by the item's template.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item to retrieve the number of uses for.</param>
|
||||||
|
/// <returns>The number of uses remaining for the item, or -1f if the item is null.</returns>
|
||||||
internal static float GetUses(this Item? item)
|
internal static float GetUses(this Item? item)
|
||||||
{
|
{
|
||||||
if (item == null)
|
if (item == null)
|
||||||
return -1f;
|
return -1f;
|
||||||
|
|
||||||
switch (item.Template)
|
switch (item.Template)
|
||||||
{
|
{
|
||||||
case KeycardTemplate:
|
case KeycardTemplate:
|
||||||
@@ -77,9 +150,15 @@ namespace LootValueEX.Extensions
|
|||||||
if (item.TryGetItemComponent(out MedKitComponent medKitComponent))
|
if (item.TryGetItemComponent(out MedKitComponent medKitComponent))
|
||||||
return medKitComponent.HpResource;
|
return medKitComponent.HpResource;
|
||||||
return -1f;
|
return -1f;
|
||||||
|
case MagazineTemplate:
|
||||||
|
MagazineClass? magazineClass = item as MagazineClass;
|
||||||
|
return magazineClass?.Count ?? -1f;
|
||||||
|
case AmmoBoxTemplate:
|
||||||
|
AmmoBox? ammoBox = item as AmmoBox;
|
||||||
|
return ammoBox?.Count ?? -1f;
|
||||||
default:
|
default:
|
||||||
return -1f;
|
return -1f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
57
Plugin/Patches/Screens/InventoryScreenPatch.cs
Normal file
57
Plugin/Patches/Screens/InventoryScreenPatch.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using EFT;
|
||||||
|
using EFT.HealthSystem;
|
||||||
|
using EFT.UI;
|
||||||
|
using LootValueEX.Extensions;
|
||||||
|
using SPT.Reflection.Patching;
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
namespace LootValueEX.Patches.Screens
|
||||||
|
{
|
||||||
|
internal class InventoryScreenPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod() => typeof(InventoryScreen).GetMethods().SingleOrDefault(method => method.Name == "Show" && method.GetParameters()[0].ParameterType == typeof(IHealthController));
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
private static void PatchPostfix(ref Profile ___profile_0, ref LootItemClass ___lootItemClass)
|
||||||
|
{
|
||||||
|
Profile profile = ___profile_0;
|
||||||
|
|
||||||
|
TaskCompletionSource<bool> tcsInventory = new();
|
||||||
|
CancellationTokenSource ctsInventory = new CancellationTokenSource(5000);
|
||||||
|
Task<bool> taskInventory = tcsInventory.Task;
|
||||||
|
Task.Factory.StartNew(async () =>
|
||||||
|
{
|
||||||
|
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()}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sw.Stop();
|
||||||
|
Plugin.Log.LogDebug($"Equipment processing finished in {sw.ElapsedMilliseconds}ms");
|
||||||
|
tcsInventory.SetResult(true);
|
||||||
|
}, ctsInventory.Token);
|
||||||
|
|
||||||
|
if(___lootItemClass != null)
|
||||||
|
{
|
||||||
|
LootItemClass lootItemClass = ___lootItemClass;
|
||||||
|
TaskCompletionSource<bool> tcsLoot = new();
|
||||||
|
CancellationTokenSource ctsLoot = new CancellationTokenSource(5000);
|
||||||
|
Task<bool> taskLoot = tcsLoot.Task;
|
||||||
|
Task.Factory.StartNew(async () =>
|
||||||
|
{
|
||||||
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
foreach (EFT.InventoryLogic.Item item in lootItemClass.GetAllItems())
|
||||||
|
{
|
||||||
|
//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);
|
||||||
|
}, ctsLoot.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using SPT.Reflection.Patching;
|
|||||||
using SPT.Reflection.Utils;
|
using SPT.Reflection.Utils;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace LootValueEX.Patches
|
namespace LootValueEX.Patches.Tooltips
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This patch will affect the following screens: Stash, Weapon Preset Builder, Character Gear, Character Preset Selector, New Ragfair Offer, Message Items, Loot
|
/// This patch will affect the following screens: Stash, Weapon Preset Builder, Character Gear, Character Preset Selector, New Ragfair Offer, Message Items, Loot
|
||||||
@@ -5,7 +5,7 @@ using SPT.Reflection.Patching;
|
|||||||
using SPT.Reflection.Utils;
|
using SPT.Reflection.Utils;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace LootValueEX.Patches
|
namespace LootValueEX.Patches.Tooltips
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This patch will affect the following screens: Stash, Weapon Preset Builder, Character Gear, Character Preset Selector, New Ragfair Offer, Message Items, Loot
|
/// This patch will affect the following screens: Stash, Weapon Preset Builder, Character Gear, Character Preset Selector, New Ragfair Offer, Message Items, Loot
|
||||||
@@ -14,7 +14,7 @@ namespace LootValueEX.Patches
|
|||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod() => typeof(GridItemView).GetMethod("ShowTooltip", BindingFlags.Instance | BindingFlags.Public);
|
protected override MethodBase GetTargetMethod() => typeof(GridItemView).GetMethod("ShowTooltip", BindingFlags.Instance | BindingFlags.Public);
|
||||||
internal static bool PatchTooltip { get; private set; } = false;
|
internal static bool PatchTooltip { get; private set; } = false;
|
||||||
internal static EFT.InventoryLogic.Item? HoveredItem { get; private set; }
|
internal static Item? HoveredItem { get; private set; }
|
||||||
|
|
||||||
[PatchPrefix]
|
[PatchPrefix]
|
||||||
static void EnableTooltipPatch(GridItemView __instance)
|
static void EnableTooltipPatch(GridItemView __instance)
|
||||||
@@ -7,7 +7,7 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LootValueEX.Patches
|
namespace LootValueEX.Patches.Tooltips
|
||||||
{
|
{
|
||||||
internal class HandbookPatching : ModulePatch
|
internal class HandbookPatching : ModulePatch
|
||||||
{
|
{
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace LootValueEX.Patches
|
namespace LootValueEX.Patches.Tooltips
|
||||||
{
|
{
|
||||||
class InsuranceGridPatch : ModulePatch
|
class InsuranceGridPatch : ModulePatch
|
||||||
{
|
{
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace LootValueEX.Patches
|
namespace LootValueEX.Patches.Tooltips
|
||||||
{
|
{
|
||||||
class InsuranceSlotPatch : ModulePatch
|
class InsuranceSlotPatch : ModulePatch
|
||||||
{
|
{
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace LootValueEX.Patches
|
namespace LootValueEX.Patches.Tooltips
|
||||||
{
|
{
|
||||||
internal class ItemPricePatch : ModulePatch
|
internal class ItemPricePatch : ModulePatch
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod() => typeof(EFT.UI.PriceTooltip).GetMethod("Show",
|
protected override MethodBase GetTargetMethod() => typeof(EFT.UI.PriceTooltip).GetMethod("Show",
|
||||||
BindingFlags.Instance | BindingFlags.Public,
|
BindingFlags.Instance | BindingFlags.Public,
|
||||||
null,
|
null,
|
||||||
[typeof(EFT.InventoryLogic.EOwnerType), typeof(string), typeof(int), typeof(string)],
|
[typeof(EFT.InventoryLogic.EOwnerType), typeof(string), typeof(int), typeof(string)],
|
||||||
@@ -10,7 +10,7 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LootValueEX.Patches
|
namespace LootValueEX.Patches.Tooltips
|
||||||
{
|
{
|
||||||
internal class TooltipPatch : ModulePatch
|
internal class TooltipPatch : ModulePatch
|
||||||
{
|
{
|
||||||
@@ -19,12 +19,10 @@ namespace LootValueEX.Patches
|
|||||||
[PatchPostfix]
|
[PatchPostfix]
|
||||||
public static void AlterText(SimpleTooltip __instance, string text)
|
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)
|
if (GridItemTooltipPatch.PatchTooltip)
|
||||||
{
|
{
|
||||||
text += $"<br>TemplateID: {GridItemTooltipPatch.HoveredItem?.TemplateId}<br>Template: {GridItemTooltipPatch.HoveredItem?.Template}<br>Item hashsum: {GridItemTooltipPatch.HoveredItem?.GetHashSum()}<br>Custom hash: {GridItemTooltipPatch.HoveredItem?.GetCustomHash()}<br>Item durability: {GridItemTooltipPatch.HoveredItem?.GetDurability()}<br>Item uses: {GridItemTooltipPatch.HoveredItem?.GetUses()}<br><color=#ff0fff><b>GridItemView</b></color>";
|
Structs.TradeOfferStruct tradeOffer = Utils.ItemUtils.GetBestTraderValue(GridItemTooltipPatch.HoveredItem);
|
||||||
Plugin.Log.LogDebug(GridItemTooltipPatch.HoveredItem?.AttributesToString());
|
text += $"<br>Hash: {GridItemTooltipPatch.HoveredItem?.GetCustomHash()}<br>Trader: {tradeOffer.TraderID}<br>Value: {tradeOffer.Price}<br>Currency: {tradeOffer.CurrencyID}<br>Price in rubles: {tradeOffer.PriceInRouble}<br><color=#ff0fff><b>GridItemView</b></color>";
|
||||||
}
|
}
|
||||||
if (InsuranceSlotPatch.PatchTooltip)
|
if (InsuranceSlotPatch.PatchTooltip)
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@ using SPT.Reflection.Patching;
|
|||||||
using SPT.Reflection.Utils;
|
using SPT.Reflection.Utils;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace LootValueEX.Patches
|
namespace LootValueEX.Patches.Tooltips
|
||||||
{
|
{
|
||||||
class TradingItemPatch : ModulePatch
|
class TradingItemPatch : ModulePatch
|
||||||
{
|
{
|
||||||
22
Plugin/Patches/TraderClassPatch.cs
Normal file
22
Plugin/Patches/TraderClassPatch.cs
Normal file
@@ -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, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,14 +11,18 @@ namespace LootValueEX
|
|||||||
{
|
{
|
||||||
Log = base.Logger;
|
Log = base.Logger;
|
||||||
|
|
||||||
new Patches.GridItemTooltipPatch().Enable();
|
new Patches.TraderClassPatch().Enable();
|
||||||
new Patches.TooltipPatch().Enable();
|
|
||||||
new Patches.InsuranceGridPatch().Enable();
|
new Patches.Tooltips.GridItemTooltipPatch().Enable();
|
||||||
new Patches.InsuranceSlotPatch().Enable();
|
new Patches.Tooltips.TooltipPatch().Enable();
|
||||||
new Patches.ItemPricePatch().Enable();
|
new Patches.Tooltips.InsuranceGridPatch().Enable();
|
||||||
new Patches.TradingItemPatch().Enable();
|
new Patches.Tooltips.InsuranceSlotPatch().Enable();
|
||||||
new Patches.HandbookPatching().Enable();
|
new Patches.Tooltips.ItemPricePatch().Enable();
|
||||||
new Patches.BarterItemPatch().Enable();
|
new Patches.Tooltips.TradingItemPatch().Enable();
|
||||||
|
new Patches.Tooltips.HandbookPatching().Enable();
|
||||||
|
new Patches.Tooltips.BarterItemPatch().Enable();
|
||||||
|
|
||||||
|
new Patches.Screens.InventoryScreenPatch().Enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
Plugin/Structs/TradeOfferStruct.cs
Normal file
23
Plugin/Structs/TradeOfferStruct.cs
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Plugin/Utils/EconomyUtils.cs
Normal file
44
Plugin/Utils/EconomyUtils.cs
Normal file
@@ -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 static class EconomyUtils
|
||||||
|
{
|
||||||
|
|
||||||
|
internal 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<HandbookClass>.Instance.GetBasePrice(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace LootValueEX.Utils
|
namespace LootValueEX.Utils
|
||||||
{
|
{
|
||||||
internal class HashingUtils
|
internal static class HashingUtils
|
||||||
{
|
{
|
||||||
internal static string ConvertToSha256(string value)
|
internal static string ConvertToSha256(string value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,17 +1,29 @@
|
|||||||
using EFT.InventoryLogic;
|
using EFT.InventoryLogic;
|
||||||
|
using LootValueEX.Extensions;
|
||||||
|
using SPT.Reflection.Utils;
|
||||||
|
|
||||||
namespace LootValueEX.Utils
|
namespace LootValueEX.Utils
|
||||||
{
|
{
|
||||||
internal class ItemUtils
|
internal static class ItemUtils
|
||||||
{
|
{
|
||||||
public static float GetArmorDurability(IEnumerable<RepairableComponent> repairableComponents)
|
internal static float GetArmorDurability(IEnumerable<RepairableComponent> repairableComponents) => repairableComponents.Sum(x => x.Durability);
|
||||||
|
|
||||||
|
internal static Task<Structs.TradeOfferStruct> GetBestTraderValueAsync(Item item)
|
||||||
{
|
{
|
||||||
float totalDurability = 0;
|
Structs.TradeOfferStruct bestOffer = new();
|
||||||
foreach (RepairableComponent component in repairableComponents)
|
item = item.UnstackItem();
|
||||||
|
foreach (TraderClass trader in ClientAppUtils.GetMainApp().GetClientBackEndSession().DisplayableTraders)
|
||||||
{
|
{
|
||||||
totalDurability += component.Durability;
|
Structs.TradeOfferStruct currentOffer = EconomyUtils.GetTraderItemOffer(trader, item);
|
||||||
|
|
||||||
|
if (currentOffer.Equals(default) || currentOffer.Price <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (currentOffer.PriceInRouble > bestOffer.PriceInRouble)
|
||||||
|
bestOffer = currentOffer;
|
||||||
}
|
}
|
||||||
return totalDurability;
|
return Task.FromResult(bestOffer);
|
||||||
}
|
}
|
||||||
|
public static Structs.TradeOfferStruct GetBestTraderValue(Item item) => Task.Run(() => GetBestTraderValueAsync(item)).Result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user