mirror of
https://github.com/sp-tarkov/modules.git
synced 2025-02-13 03:30:44 -05:00
Fix the server sell all price not matching the client displayed value (!77)
- Add a patch that's triggered when the item list for the scav post-raid screen is populated that stores the calculated item value - Add a second patch that's triggered when the player confirms a Sell All, that uses a custom request object that contains the previously calculated sell price I made these patches not directly depend on any GClass/Class names to avoid having to update them with new client versions, this did make the code a bit harder to follow, but I think it's still readable. Requires server PR: SPT-AKI/Server#216 Resolves: SPT-AKI/Issues#410 Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Reviewed-on: SPT-AKI/Modules#77 Co-authored-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com> Co-committed-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com>
This commit is contained in:
parent
dc4aee0e6b
commit
2e11148618
@ -60,6 +60,8 @@ namespace Aki.SinglePlayer
|
||||
new MidRaidAchievementChangePatch().Enable();
|
||||
new GetTraderServicesPatch().Enable();
|
||||
new PurchaseTraderServicePatch().Enable();
|
||||
new ScavSellAllPriceStorePatch().Enable();
|
||||
new ScavSellAllRequestPatch().Enable();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
20
project/Aki.SinglePlayer/Models/ScavMode/SellAllRequest.cs
Normal file
20
project/Aki.SinglePlayer/Models/ScavMode/SellAllRequest.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using EFT.InventoryLogic.BackendInventoryInteraction;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Aki.SinglePlayer.Models.ScavMode
|
||||
{
|
||||
public class SellAllRequest
|
||||
{
|
||||
[JsonProperty("Action")]
|
||||
public string Action;
|
||||
|
||||
[JsonProperty("totalValue")]
|
||||
public int TotalValue;
|
||||
|
||||
[JsonProperty("fromOwner")]
|
||||
public OwnerInfo FromOwner;
|
||||
|
||||
[JsonProperty("toOwner")]
|
||||
public OwnerInfo ToOwner;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
using Aki.Reflection.Patching;
|
||||
using EFT.InventoryLogic;
|
||||
using EFT.UI;
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Aki.SinglePlayer.Patches.ScavMode
|
||||
{
|
||||
/**
|
||||
* After fetching the list of items for the post-raid scav inventory screen, calculate
|
||||
* the total "Sell All" value, and store it for retrieval if the user hits "Sell All"
|
||||
*/
|
||||
public class ScavSellAllPriceStorePatch : ModulePatch
|
||||
{
|
||||
private static string FENCE_ID = "579dc571d53a0658a154fbec";
|
||||
private static string ROUBLE_TID = "5449016a4bdc2d6f028b456f";
|
||||
|
||||
private static FieldInfo _sessionField;
|
||||
|
||||
public static int StoredPrice;
|
||||
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
Type scavInventoryScreenType = typeof(ScavengerInventoryScreen);
|
||||
_sessionField = AccessTools.GetDeclaredFields(scavInventoryScreenType).FirstOrDefault(f => f.FieldType == typeof(ISession));
|
||||
|
||||
return AccessTools.FirstMethod(scavInventoryScreenType, IsTargetMethod);
|
||||
}
|
||||
|
||||
private bool IsTargetMethod(MethodBase method)
|
||||
{
|
||||
// Look for a method with one parameter named `items`
|
||||
// method_3(out IEnumerable<Item> items)
|
||||
if (method.GetParameters().Length == 1 && method.GetParameters()[0].Name == "items")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
private static void PatchPostfix(ScavengerInventoryScreen __instance, IEnumerable<Item> items)
|
||||
{
|
||||
ISession session = _sessionField.GetValue(__instance) as ISession;
|
||||
TraderClass traderClass = session.Traders.FirstOrDefault(x => x.Id == FENCE_ID);
|
||||
|
||||
int totalPrice = 0;
|
||||
foreach (Item item in items)
|
||||
{
|
||||
if (item.TemplateId == ROUBLE_TID)
|
||||
{
|
||||
totalPrice += item.StackObjectsCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
totalPrice += traderClass.GetItemPriceOnScavSell(item, true);
|
||||
}
|
||||
}
|
||||
|
||||
StoredPrice = totalPrice;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
using Aki.Reflection.Patching;
|
||||
using Aki.SinglePlayer.Models.ScavMode;
|
||||
using Comfort.Common;
|
||||
using EFT.InventoryLogic.BackendInventoryInteraction;
|
||||
using EFT.InventoryLogic;
|
||||
using HarmonyLib;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using Aki.Reflection.Utils;
|
||||
|
||||
namespace Aki.SinglePlayer.Patches.ScavMode
|
||||
{
|
||||
/**
|
||||
* When the user clicks "Sell All" after a scav raid, create a custom request object
|
||||
* that includes the calculated sell price
|
||||
*/
|
||||
public class ScavSellAllRequestPatch : ModulePatch
|
||||
{
|
||||
private static MethodInfo _sendOperationMethod;
|
||||
private string TargetMethodName = "SellAllFromSavage";
|
||||
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
// We want to find a type that contains `SellAllFromSavage` but doesn't extend from `IBackendStatus`
|
||||
Type targetType = PatchConstants.EftTypes.SingleCustom(IsTargetType);
|
||||
|
||||
Logger.LogDebug($"{this.GetType().Name} Type: {targetType?.Name}");
|
||||
|
||||
// So we can call "SendOperationRightNow" without directly referencing a GClass
|
||||
_sendOperationMethod = AccessTools.Method(targetType, "SendOperationRightNow");
|
||||
|
||||
return AccessTools.Method(targetType, TargetMethodName);
|
||||
}
|
||||
|
||||
private bool IsTargetType(Type type)
|
||||
{
|
||||
// Isn't an interface, isn't part of the dummy class, and contains our target method
|
||||
if (!type.IsInterface
|
||||
&& type.DeclaringType != typeof(BackendDummyClass)
|
||||
&& type.GetMethod(TargetMethodName) != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
private static bool PatchPrefix(object __instance, ref Task<IResult> __result, string playerId, string petId)
|
||||
{
|
||||
// Build request with additional information
|
||||
OwnerInfo fromOwner = new OwnerInfo
|
||||
{
|
||||
Id = petId,
|
||||
Type = EOwnerType.Profile
|
||||
};
|
||||
OwnerInfo toOwner = new OwnerInfo
|
||||
{
|
||||
Id = playerId,
|
||||
Type = EOwnerType.Profile
|
||||
};
|
||||
|
||||
SellAllRequest request = new SellAllRequest
|
||||
{
|
||||
Action = "SellAllFromSavage",
|
||||
TotalValue = ScavSellAllPriceStorePatch.StoredPrice, // Retrieve value stored in earlier patch
|
||||
FromOwner = fromOwner, // Scav
|
||||
ToOwner = toOwner // PMC
|
||||
};
|
||||
|
||||
// We'll re-use the same logic/methods that the base code used
|
||||
TaskCompletionSource<IResult> taskCompletionSource = new TaskCompletionSource<IResult>();
|
||||
_sendOperationMethod.Invoke(__instance, new object[] { request, new Callback(taskCompletionSource.SetResult) });
|
||||
__result = taskCompletionSource.Task;
|
||||
|
||||
// Skip original
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user