0
0
mirror of https://github.com/sp-tarkov/modules.git synced 2025-02-13 09:50:43 -05:00
modules/project/Aki.SinglePlayer/Patches/ScavMode/ScavSellAllRequestPatch.cs
DrakiaXYZ 2e11148618 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>
2024-02-08 09:11:22 +00:00

82 lines
2.9 KiB
C#

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;
}
}
}