2023-03-03 16:23:46 +01:00
import { inject , injectable } from "tsyringe" ;
2023-10-19 19:21:17 +02:00
import { InventoryHelper } from "@spt-aki/helpers/InventoryHelper" ;
import { ItemHelper } from "@spt-aki/helpers/ItemHelper" ;
import { TraderHelper } from "@spt-aki/helpers/TraderHelper" ;
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData" ;
import { Item , Upd } from "@spt-aki/models/eft/common/tables/IItem" ;
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse" ;
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData" ;
import { IProcessSellTradeRequestData } from "@spt-aki/models/eft/trade/IProcessSellTradeRequestData" ;
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes" ;
import { Traders } from "@spt-aki/models/enums/Traders" ;
import { ITraderConfig } from "@spt-aki/models/spt/config/ITraderConfig" ;
import { ILogger } from "@spt-aki/models/spt/utils/ILogger" ;
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder" ;
import { ConfigServer } from "@spt-aki/servers/ConfigServer" ;
import { RagfairServer } from "@spt-aki/servers/RagfairServer" ;
import { FenceService } from "@spt-aki/services/FenceService" ;
import { PaymentService } from "@spt-aki/services/PaymentService" ;
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil" ;
2023-03-03 16:23:46 +01:00
@injectable ( )
export class TradeHelper
{
protected traderConfig : ITraderConfig ;
constructor (
@inject ( "WinstonLogger" ) protected logger : ILogger ,
@inject ( "EventOutputHolder" ) protected eventOutputHolder : EventOutputHolder ,
@inject ( "TraderHelper" ) protected traderHelper : TraderHelper ,
@inject ( "ItemHelper" ) protected itemHelper : ItemHelper ,
@inject ( "PaymentService" ) protected paymentService : PaymentService ,
@inject ( "FenceService" ) protected fenceService : FenceService ,
2023-05-24 16:51:05 +02:00
@inject ( "HttpResponseUtil" ) protected httpResponse : HttpResponseUtil ,
2023-03-03 16:23:46 +01:00
@inject ( "InventoryHelper" ) protected inventoryHelper : InventoryHelper ,
@inject ( "RagfairServer" ) protected ragfairServer : RagfairServer ,
2023-11-16 22:42:06 +01:00
@inject ( "ConfigServer" ) protected configServer : ConfigServer ,
2023-03-03 16:23:46 +01:00
)
{
this . traderConfig = this . configServer . getConfig ( ConfigTypes . TRADER ) ;
}
/ * *
* Buy item from flea or trader
* @param pmcData Player profile
* @param buyRequestData data from client
* @param sessionID Session id
* @param foundInRaid Should item be found in raid
* @param upd optional item details used when buying from flea
2023-11-16 22:42:06 +01:00
* @returns
2023-03-03 16:23:46 +01:00
* /
2023-11-16 22:42:06 +01:00
public buyItem (
pmcData : IPmcData ,
buyRequestData : IProcessBuyTradeRequestData ,
sessionID : string ,
foundInRaid : boolean ,
upd : Upd ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
let output = this . eventOutputHolder . getOutput ( sessionID ) ;
const newReq = {
2023-11-16 22:42:06 +01:00
items : [ {
// eslint-disable-next-line @typescript-eslint/naming-convention
item_id : buyRequestData.item_id ,
count : buyRequestData.count ,
} ] ,
tid : buyRequestData.tid ,
2023-03-03 16:23:46 +01:00
} ;
const callback = ( ) = >
{
let itemPurchased : Item ;
const isRagfair = buyRequestData . tid . toLocaleLowerCase ( ) === "ragfair" ;
if ( isRagfair )
{
const allOffers = this . ragfairServer . getOffers ( ) ;
2023-11-16 22:42:06 +01:00
const offersWithItem = allOffers . find ( ( x ) = > x . items [ 0 ] . _id === buyRequestData . item_id ) ;
2023-03-03 16:23:46 +01:00
itemPurchased = offersWithItem . items [ 0 ] ;
}
else
{
2023-10-10 13:03:20 +02:00
const traderAssorts = this . traderHelper . getTraderAssortsByTraderId ( buyRequestData . tid ) . items ;
2023-11-16 22:42:06 +01:00
itemPurchased = traderAssorts . find ( ( x ) = > x . _id === buyRequestData . item_id ) ;
2023-03-03 16:23:46 +01:00
}
// Ensure purchase does not exceed trader item limit
const hasBuyRestrictions = this . itemHelper . hasBuyRestrictions ( itemPurchased ) ;
if ( hasBuyRestrictions )
{
this . checkPurchaseIsWithinTraderItemLimit ( itemPurchased , buyRequestData . item_id , buyRequestData . count ) ;
}
// Decrement trader item count
if ( ! isRagfair )
{
itemPurchased . upd . StackObjectsCount -= buyRequestData . count ;
}
if ( this . traderConfig . persistPurchaseDataInProfile && hasBuyRestrictions )
{
this . traderHelper . addTraderPurchasesToPlayerProfile ( sessionID , newReq ) ;
}
/// Pay for item
output = this . paymentService . payMoney ( pmcData , buyRequestData , sessionID , output ) ;
if ( output . warnings . length > 0 )
{
throw new Error ( ` Transaction failed: ${ output . warnings [ 0 ] . errmsg } ` ) ;
}
if ( buyRequestData . tid === Traders . FENCE )
{
// Bought fence offer, remove from listing
this . fenceService . removeFenceOffer ( buyRequestData . item_id ) ;
}
2023-10-10 13:03:20 +02:00
else if ( hasBuyRestrictions )
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
// Increment non-fence trader item buy count
this . incrementAssortBuyCount ( itemPurchased , buyRequestData . count ) ;
2023-03-03 16:23:46 +01:00
}
2023-12-14 16:47:01 +01:00
this . logger . debug ( ` Bought item: ${ buyRequestData . item_id } from: ${ Traders [ buyRequestData . tid ] } ` ) ;
2023-03-03 16:23:46 +01:00
} ;
return this . inventoryHelper . addItem ( pmcData , newReq , output , sessionID , callback , foundInRaid , upd ) ;
}
/ * *
* Sell item to trader
2023-10-10 13:03:20 +02:00
* @param profileWithItemsToSell Profile to remove items from
* @param profileToReceiveMoney Profile to accept the money for selling item
2023-05-24 16:51:05 +02:00
* @param sellRequest Request data
2023-03-03 16:23:46 +01:00
* @param sessionID Session id
* @returns IItemEventRouterResponse
* /
2023-11-16 22:42:06 +01:00
public sellItem (
profileWithItemsToSell : IPmcData ,
profileToReceiveMoney : IPmcData ,
sellRequest : IProcessSellTradeRequestData ,
sessionID : string ,
) : IItemEventRouterResponse
2023-03-03 16:23:46 +01:00
{
let output = this . eventOutputHolder . getOutput ( sessionID ) ;
2023-05-24 16:51:05 +02:00
// Find item in inventory and remove it
for ( const itemToBeRemoved of sellRequest . items )
2023-03-03 16:23:46 +01:00
{
2023-05-24 16:51:05 +02:00
const itemIdToFind = itemToBeRemoved . id . replace ( /\s+/g , "" ) ; // Strip out whitespace
2023-03-03 16:23:46 +01:00
2023-05-24 16:51:05 +02:00
// Find item in player inventory, or show error to player if not found
2023-11-16 22:42:06 +01:00
const matchingItemInInventory = profileWithItemsToSell . Inventory . items . find ( ( x ) = > x . _id === itemIdToFind ) ;
2023-05-24 16:51:05 +02:00
if ( ! matchingItemInInventory )
{
const errorMessage = ` Unable to sell item ${ itemToBeRemoved . id } , cannot be found in player inventory ` ;
this . logger . error ( errorMessage ) ;
2023-03-03 16:23:46 +01:00
2023-05-24 16:51:05 +02:00
return this . httpResponse . appendErrorToOutput ( output , errorMessage ) ;
2023-03-03 16:23:46 +01:00
}
2023-05-24 16:51:05 +02:00
this . logger . debug ( ` Selling: id: ${ matchingItemInInventory . _id } tpl: ${ matchingItemInInventory . _tpl } ` ) ;
2023-10-10 13:03:20 +02:00
// Also removes children
output = this . inventoryHelper . removeItem ( profileWithItemsToSell , itemToBeRemoved . id , sessionID , output ) ;
2023-03-03 16:23:46 +01:00
}
2023-05-24 16:51:05 +02:00
// Give player money for sold item(s)
2023-10-10 13:03:20 +02:00
return this . paymentService . getMoney ( profileToReceiveMoney , sellRequest . price , sellRequest , output , sessionID ) ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Increment the assorts buy count by number of items purchased
* Show error on screen if player attempts to buy more than what the buy max allows
* @param assortBeingPurchased assort being bought
* @param itemsPurchasedCount number of items being bought
* /
protected incrementAssortBuyCount ( assortBeingPurchased : Item , itemsPurchasedCount : number ) : void
{
assortBeingPurchased . upd . BuyRestrictionCurrent += itemsPurchasedCount ;
if ( assortBeingPurchased . upd . BuyRestrictionCurrent > assortBeingPurchased . upd . BuyRestrictionMax )
{
2023-10-10 13:03:20 +02:00
throw new Error ( "Unable to purchase item, Purchase limit reached" ) ;
2023-03-03 16:23:46 +01:00
}
}
2023-12-14 16:47:01 +01:00
/ * *
* Traders allow a limited number of purchases per refresh cycle ( default 60 mins )
* @param assortBeingPurchased the item from trader being bought
* @param assortId Id of assort being purchased
* @param count How many are being bought
* /
2023-03-03 16:23:46 +01:00
protected checkPurchaseIsWithinTraderItemLimit ( assortBeingPurchased : Item , assortId : string , count : number ) : void
{
if ( ( assortBeingPurchased . upd . BuyRestrictionCurrent + count ) > assortBeingPurchased . upd ? . BuyRestrictionMax )
{
2023-11-16 22:42:06 +01:00
throw new Error (
2023-12-14 16:47:01 +01:00
` Unable to purchase ${ count } items, this would exceed your purchase limit of ${ assortBeingPurchased . upd . BuyRestrictionMax } from the traders assort: ${ assortId } this refresh ` ,
2023-11-16 22:42:06 +01:00
) ;
2023-03-03 16:23:46 +01:00
}
}
2023-11-16 22:42:06 +01:00
}