remove dupe type, finish off paymentService, add getItemAsProduct method to itemhelper

This commit is contained in:
CWX
2025-01-23 11:20:34 +00:00
parent f09c6f6088
commit 1dc2fec880
4 changed files with 352 additions and 35 deletions
+14
View File
@@ -2,6 +2,7 @@ using System.Text.Json.Serialization;
using SptCommon.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.ItemEvent;
using Core.Models.Enums;
using Core.Models.Utils;
using Core.Services;
@@ -2013,6 +2014,19 @@ public class ItemHelper(
}
}
}
public Product GetProductFromItem(Item item)
{
return new Product
{
Id = item.Id,
Template = item.Template,
ParentId = item.ParentId,
SlotId = item.SlotId,
Location = item.Location,
Upd = item.Upd,
};
}
}
public class ItemSize
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Ragfair;
using Core.Models.Enums;
using Core.Utils.Json;
@@ -620,18 +621,6 @@ public record Production // use this instead of productive and scavcase
public string? RecipeId { get; set; }
}
public record Product
{
[JsonPropertyName("_id")]
public string? Id { get; set; }
[JsonPropertyName("_tpl")]
public string? Template { get; set; }
[JsonPropertyName("upd")]
public Upd? Upd { get; set; }
}
public record BotHideoutArea
{
[JsonPropertyName("type")]
@@ -182,7 +182,7 @@ public record Product
public string? SlotId { get; set; }
[JsonPropertyName("location")]
public ItemLocation? Location { get; set; }
public object? Location { get; set; }
[JsonPropertyName("upd")]
public Upd? Upd { get; set; }
+336 -22
View File
@@ -1,14 +1,224 @@
using SptCommon.Annotations;
using Core.Helpers;
using SptCommon.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Inventory;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Trade;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Models.Utils;
using Core.Servers;
using Core.Utils;
using Product = Core.Models.Eft.ItemEvent.Product;
namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class PaymentService
public class PaymentService(
ISptLogger<PaymentService> _logger,
HashUtil _hashUtil,
HttpResponseUtil _httpResponseUtil,
DatabaseService _databaseService,
HandbookHelper _handbookHelper,
TraderHelper _traderHelper,
ItemHelper _itemHelper,
InventoryHelper _inventoryHelper,
LocalisationService _localisationService,
PaymentHelper _paymentHelper,
ConfigServer _configServer
)
{
protected InventoryConfig _inventoryConfig = _configServer.GetConfig<InventoryConfig>();
/**
* Take money and insert items into return to server request
* @param pmcData Pmc profile
* @param request Buy item request
* @param sessionID Session id
* @param output Client response
*/
public void PayMoney(PmcData pmcData, ProcessBuyTradeRequestData request, string sessionID, ItemEventRouterResponse output)
{
// May need to convert to trader currency
var trader = _traderHelper.GetTrader(request.TransactionId, sessionID);
var payToTrader = _traderHelper.TraderEnumHasValue(request.TransactionId);
// Track the amounts of each type of currency involved in the trade.
Dictionary<string, double?> currencyAmounts = new Dictionary<string, double?>();
// Delete barter items and track currencies
foreach (var itemRequest in request.SchemeItems) {
// Find the corresponding item in the player's inventory.
var item = pmcData.Inventory.Items.FirstOrDefault((i) => i.Id == itemRequest.Id);
if (item is not null) {
if (!_paymentHelper.IsMoneyTpl(item.Template)) {
// If the item is not money, remove it from the inventory.
_inventoryHelper.RemoveItemByCount(
pmcData,
item.Id,
(int)itemRequest.Count,
sessionID,
output
);
itemRequest.Count = 0;
} else {
// If the item is money, add its count to the currencyAmounts object.
currencyAmounts.TryAdd(item.Template, (currencyAmounts[item.Template] ?? 0) + itemRequest.Count);
}
} else {
// Used by `SptInsure`
// Handle differently, `id` is the money type tpl
var currencyTpl = itemRequest.Id;
currencyAmounts.TryAdd(currencyTpl, (currencyAmounts[currencyTpl] ?? 0) + itemRequest.Count);
}
}
// Track the total amount of all currencies.
var totalCurrencyAmount = 0;
// Loop through each type of currency involved in the trade.
foreach (var currencyTpl in currencyAmounts) {
var currencyAmount = currencyTpl.Value;
totalCurrencyAmount += (int)currencyAmount;
if (currencyAmount > 0) {
// Find money stacks in inventory and remove amount needed + update output object to inform client of changes
AddPaymentToOutput(pmcData, currencyTpl.Key, (int)currencyAmount, sessionID, output);
// If there are warnings, exit early.
if (output.Warnings.Count > 0) {
return;
}
if (payToTrader) {
// Convert the amount to the trader's currency and update the sales sum.
var costOfPurchaseInCurrency = _handbookHelper.FromRUB(
_handbookHelper.InRUB(currencyAmount ?? 0, currencyTpl.Key),
_paymentHelper.GetCurrency(trader.Currency)
);
// Only update traders
pmcData.TradersInfo[request.TransactionId].SalesSum += costOfPurchaseInCurrency;
}
}
}
// If no currency-based payment is involved, handle it separately
if (totalCurrencyAmount == 0 && payToTrader) {
_logger.Debug(_localisationService.GetText("payment-zero_price_no_payment"));
// Convert the handbook price to the trader's currency and update the sales sum.
var costOfPurchaseInCurrency = _handbookHelper.FromRUB(
GetTraderItemHandbookPriceRouble(request.ItemId, request.TransactionId) ?? 0,
_paymentHelper.GetCurrency(trader.Currency)
);
pmcData.TradersInfo[request.TransactionId].SalesSum += costOfPurchaseInCurrency;
}
if (payToTrader) {
_traderHelper.LevelUp(request.TransactionId, pmcData);
}
_logger.Debug("Item(s) taken. Status OK.");
}
private double? GetTraderItemHandbookPriceRouble(string? traderAssortId, string traderId)
{
var purchasedAssortItem = _traderHelper.GetTraderAssortItemByAssortId(traderId, traderAssortId);
if (purchasedAssortItem is null) {
return 1;
}
var assortItemPriceRouble = _handbookHelper.GetTemplatePrice(purchasedAssortItem.Template);
if (assortItemPriceRouble is null) {
_logger.Debug($"No item price found for {purchasedAssortItem.Template} on trader: {traderId} in assort: {traderAssortId}");
return 1;
}
return assortItemPriceRouble;
}
public void GiveProfileMoney(PmcData pmcData, double? amountToSend, ProcessSellTradeRequestData request,
ItemEventRouterResponse output, string sessionID)
{
var trader = _traderHelper.GetTrader(request.TransactionId, sessionID);
if (trader is null) {
_logger.Error($"Unable to add currency to profile as trader: {request.TransactionId} does not exist");
return;
}
var currencyTpl = _paymentHelper.GetCurrency(trader.Currency);
var calcAmount = _handbookHelper.FromRUB(_handbookHelper.InRUB(amountToSend ?? 0, currencyTpl), currencyTpl);
var currencyMaxStackSize = _itemHelper.GetItem(currencyTpl).Value.Properties?.StackMaxSize;
if (currencyMaxStackSize is null) {
_logger.Error($"Unable to add currency: {currencyTpl} to profile as it lacks a _props property");
return;
}
var skipSendingMoneyToStash = false;
foreach (var item in pmcData.Inventory.Items) {
// Item is not currency
if (item.Template != currencyTpl) {
continue;
}
// Item is not in the stash
if (!_inventoryHelper.IsItemInStash(pmcData, item)) {
continue;
}
// Found currency item
if (item.Upd.StackObjectsCount < currencyMaxStackSize) {
if (item.Upd.StackObjectsCount + calcAmount > currencyMaxStackSize) {
// calculate difference
calcAmount -= (currencyMaxStackSize - item.Upd.StackObjectsCount) ?? 0;
item.Upd.StackObjectsCount = currencyMaxStackSize;
} else {
skipSendingMoneyToStash = true;
item.Upd.StackObjectsCount += calcAmount;
}
// Inform client of change to items StackObjectsCount
output.ProfileChanges[sessionID].Items.ChangedItems.Add(_itemHelper.GetProductFromItem(item));
if (skipSendingMoneyToStash) {
break;
}
}
}
// Create single currency item with all currency on it
Item rootCurrencyReward = new Item {
Id = _hashUtil.Generate(),
Template = currencyTpl,
Upd = new Upd { StackObjectsCount = Math.Round(calcAmount) },
};
// Ensure money is properly split to follow its max stack size limit
var rewards = _itemHelper.SplitStackIntoSeparateItems(rootCurrencyReward);
if (!skipSendingMoneyToStash) {
AddItemsDirectRequest addItemToStashRequest = new AddItemsDirectRequest {
ItemsWithModsToAdd = rewards,
FoundInRaid = false,
Callback = null,
UseSortingTable = true,
};
_inventoryHelper.AddItemsToStash(sessionID, addItemToStashRequest, pmcData, output);
}
// Calcualte new total sale sum with trader item sold to
var saleSum = pmcData.TradersInfo[request.TransactionId].SalesSum + amountToSend;
pmcData.TradersInfo[request.TransactionId].SalesSum = saleSum;
_traderHelper.LevelUp(request.TransactionId, pmcData);
}
/**
* Remove currency from player stash/inventory and update client object with changes
* @param pmcData Player profile to find and remove currency from
@@ -20,12 +230,59 @@ public class PaymentService
public void AddPaymentToOutput(
PmcData pmcData,
string currencyTpl,
decimal amountToPay,
double amountToPay,
string sessionID,
ItemEventRouterResponse output
)
{
throw new NotImplementedException();
var moneyItemsInInventory = GetSortedMoneyItemsInInventory(
pmcData,
currencyTpl,
pmcData.Inventory.Stash
);
//Ensure all money items found have a upd
foreach (var moneyStack in moneyItemsInInventory) {
moneyStack.Upd ??= new Upd { StackObjectsCount = 1 };
}
var amountAvailable = moneyItemsInInventory.Aggregate(0,
(accumulator, item) => (int)(accumulator + item.Upd.StackObjectsCount)
);
// If no money in inventory or amount is not enough we return false
if (moneyItemsInInventory.Count <= 0 || amountAvailable < amountToPay) {
_logger.Error(
_localisationService.GetText("payment-not_enough_money_to_complete_transation", new {
amountToPay = amountToPay,
amountAvailable = amountAvailable,
})
);
_httpResponseUtil.AppendErrorToOutput(
output,
_localisationService.GetText("payment-not_enough_money_to_complete_transation_short", amountToPay),
BackendErrorCodes.UnknownTradingError
);
return;
}
var leftToPay = amountToPay;
foreach (var profileMoneyItem in moneyItemsInInventory) {
var itemAmount = profileMoneyItem.Upd.StackObjectsCount;
if (leftToPay >= itemAmount) {
leftToPay -= itemAmount ?? 0;
_inventoryHelper.RemoveItem(pmcData, profileMoneyItem.Id, sessionID, output);
} else {
profileMoneyItem.Upd.StackObjectsCount -= leftToPay;
leftToPay = 0;
output.ProfileChanges[sessionID].Items.ChangedItems.Add(_itemHelper.GetProductFromItem(profileMoneyItem));
}
if (leftToPay == 0) {
break;
}
}
}
/**
@@ -38,7 +295,15 @@ public class PaymentService
*/
protected List<Item> GetSortedMoneyItemsInInventory(PmcData pmcData, string currencyTpl, string playerStashId)
{
throw new NotImplementedException();
var moneyItemsInInventory = _itemHelper.FindBarterItems("tpl", pmcData.Inventory.Items, currencyTpl);
if (moneyItemsInInventory.Count == 0) {
_logger.Debug($"No {currencyTpl} money items found in inventory");
}
// Prioritise items in stash to top of array
moneyItemsInInventory.Sort((a, b) => PrioritiseStashSort(a, b, pmcData.Inventory.Items, playerStashId));
return moneyItemsInInventory;
}
/**
@@ -52,7 +317,59 @@ public class PaymentService
*/
protected int PrioritiseStashSort(Item a, Item b, List<Item> inventoryItems, string playerStashId)
{
throw new NotImplementedException();
// a in root of stash, prioritise
if (a.ParentId == playerStashId && b.ParentId != playerStashId) {
return -1;
}
// b in root stash, prioritise
if (a.ParentId != playerStashId && b.ParentId == playerStashId) {
return 1;
}
// both in containers
if (a.SlotId == "main" && b.SlotId == "main") {
// Both items are in containers
var aInStash = this.IsInStash(a.ParentId, inventoryItems, playerStashId);
var bInStash = this.IsInStash(b.ParentId, inventoryItems, playerStashId);
// a in stash in container, prioritise
if (aInStash && !bInStash) {
return -1;
}
// b in stash in container, prioritise
if (!aInStash && bInStash) {
return 1;
}
// Both in stash in containers
if (aInStash && bInStash) {
// Containers where taking money from would inconvinence player
var deprioritisedContainers = _inventoryConfig.DeprioritisedMoneyContainers;
var aImmediateParent = inventoryItems.FirstOrDefault((item) => item.Id == a.ParentId);
var bImmediateParent = inventoryItems.FirstOrDefault((item) => item.Id == b.ParentId);
// A is not a deprioritised container, B is
if (
!deprioritisedContainers.Contains(aImmediateParent.Template) &&
deprioritisedContainers.Contains(bImmediateParent.Template)
) {
return -1;
}
// B is not a deprioritised container, A is
if (
deprioritisedContainers.Contains(aImmediateParent.Template) &&
!deprioritisedContainers.Contains(bImmediateParent.Template)
) {
return 1;
}
}
}
// they match
return 0;
}
/**
@@ -64,23 +381,20 @@ public class PaymentService
*/
protected bool IsInStash(string itemId, List<Item> inventoryItems, string playerStashId)
{
throw new NotImplementedException();
}
var itemParent = inventoryItems.FirstOrDefault((item) => item.Id == itemId);
/**
* Take money and insert items into return to server request
* @param pmcData Pmc profile
* @param request Buy item request
* @param sessionID Session id
* @param output Client response
*/
public void PayMoney(PmcData pmcData, ProcessBuyTradeRequestData request, string sessionID, ItemEventRouterResponse output)
{
throw new NotImplementedException();
}
if (itemParent is not null) {
if (itemParent.SlotId == "hideout") {
return true;
}
public void GiveProfileMoney(PmcData profileToReceiveMoney, double? sellRequestPrice, ProcessSellTradeRequestData sellRequest, ItemEventRouterResponse output, string sessionId)
{
throw new NotImplementedException();
if (itemParent.Id == playerStashId) {
return true;
}
return IsInStash(itemParent.ParentId, inventoryItems, playerStashId);
}
return false;
}
}