diff --git a/project/src/controllers/TradeController.ts b/project/src/controllers/TradeController.ts index a081d78b..d8885a89 100644 --- a/project/src/controllers/TradeController.ts +++ b/project/src/controllers/TradeController.ts @@ -8,9 +8,10 @@ import { IPmcData } from "@spt-aki/models/eft/common/IPmcData"; import { Item, Upd } from "@spt-aki/models/eft/common/tables/IItem"; import { ITraderBase } from "@spt-aki/models/eft/common/tables/ITrader"; import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; +import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer"; import { IProcessBaseTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBaseTradeRequestData"; import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData"; -import { IProcessRagfairTradeRequestData } from "@spt-aki/models/eft/trade/IProcessRagfairTradeRequestData"; +import { IOfferRequest, IProcessRagfairTradeRequestData } from "@spt-aki/models/eft/trade/IProcessRagfairTradeRequestData"; import { IProcessSellTradeRequestData } from "@spt-aki/models/eft/trade/IProcessSellTradeRequestData"; import { ISellScavItemsToFenceRequestData } from "@spt-aki/models/eft/trade/ISellScavItemsToFenceRequestData"; import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes"; @@ -60,19 +61,39 @@ export class TradeController sessionID: string, ): IItemEventRouterResponse { - return this.confirmTradingInternal(pmcData, request, sessionID, this.traderConfig.purchasesAreFoundInRaid); + const output = this.eventOutputHolder.getOutput(sessionID); + + // Buying + if (request.type === "buy_from_trader") + { + const foundInRaid = this.traderConfig.purchasesAreFoundInRaid; + const buyData = request; + return this.tradeHelper.buyItem(pmcData, buyData, sessionID, foundInRaid, output); + } + + // Selling + if (request.type === "sell_to_trader") + { + const sellData = request; + return this.tradeHelper.sellItem(pmcData, pmcData, sellData, sessionID); + } + + const errorMessage = `Unhandled trade event: ${request.type}`; + this.logger.error(errorMessage); + + return this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.RAGFAIRUNAVAILABLE); } /** Handle RagFairBuyOffer event */ public confirmRagfairTrading( pmcData: IPmcData, - body: IProcessRagfairTradeRequestData, + request: IProcessRagfairTradeRequestData, sessionID: string, ): IItemEventRouterResponse { - let output = this.eventOutputHolder.getOutput(sessionID); + const output = this.eventOutputHolder.getOutput(sessionID); - for (const offer of body.offers) + for (const offer of request.offers) { const fleaOffer = this.ragfairServer.getOffer(offer.id); if (!fleaOffer) @@ -94,59 +115,110 @@ export class TradeController } const sellerIsTrader = fleaOffer.user.memberType === MemberCategory.TRADER; - - // Skip buying items when player doesn't have needed loyalty - if (sellerIsTrader && fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel) + if (sellerIsTrader) { - this.logger.debug( - `Unable to buy item: ${ - fleaOffer.items[0]._tpl - } from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`, - ); - - continue; + this.buyTraderItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output); + } + else + { + this.buyPmcItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output); } - // Log full purchase JSON - this.logger.debug(this.jsonUtil.serializeAdvanced(offer, null, 2)); - - const buyData: IProcessBuyTradeRequestData = { - Action: "TradingConfirm", - type: "buy_from_trader", - tid: (sellerIsTrader) ? fleaOffer.user.id : "ragfair", - // eslint-disable-next-line @typescript-eslint/naming-convention - item_id: (sellerIsTrader) ? fleaOffer.root : fleaOffer._id, // Store ragfair offerId in buyRequestData.item_id - count: offer.count, - // eslint-disable-next-line @typescript-eslint/naming-convention - scheme_id: 0, - // eslint-disable-next-line @typescript-eslint/naming-convention - scheme_items: offer.items, - }; - - // Trader / PMC offers use different found in raid config values - const purchasedItemFoundInRaid = (sellerIsTrader) - ? this.traderConfig.purchasesAreFoundInRaid - : this.ragfairConfig.dynamic.purchasesAreFoundInRaid; - - // confirmTrading() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist for confirmTrading() to use - output = this.confirmTradingInternal( - pmcData, - buyData, - sessionID, - purchasedItemFoundInRaid, - fleaOffer.items[0].upd, - ); - - if (!sellerIsTrader) + // Exit loop early if problem found + if (output.warnings) { - // Remove the item purchased from flea offer - this.ragfairServer.removeOfferStack(fleaOffer._id, offer.count); + return output; } } return output; } + /** + * Buy an item off the flea sold by a trader + * @param sessionId Session id + * @param pmcData Player profile + * @param fleaOffer Offer being purchased + * @param requestOffer request data from client + * @param output Output to send back to client + * @returns IItemEventRouterResponse + */ + protected buyTraderItemFromRagfair(sessionId: string, pmcData: IPmcData, fleaOffer: IRagfairOffer, requestOffer: IOfferRequest, output: IItemEventRouterResponse): IItemEventRouterResponse + { + // Skip buying items when player doesn't have needed loyalty + if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData)) + { + const errorMessage = `Unable to buy item: ${ + fleaOffer.items[0]._tpl + } from trader: ${fleaOffer.user.id} as loyalty level too low, skipping` + this.logger.debug(errorMessage); + + return this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.RAGFAIRUNAVAILABLE); + } + + const buyData: IProcessBuyTradeRequestData = { + Action: "TradingConfirm", + type: "buy_from_ragfair", + tid: fleaOffer.user.id, + item_id: fleaOffer.root, + count: requestOffer.count, + scheme_id: 0, + scheme_items: requestOffer.items, + }; + + this.tradeHelper.buyItem(pmcData, buyData, sessionId, this.traderConfig.purchasesAreFoundInRaid, output); + + return output; + } + + /** + * Buy an item off the flea sold by a PMC + * @param sessionId Session id + * @param pmcData Player profile + * @param fleaOffer Offer being purchased + * @param requestOffer Request data from client + * @param output Output to send back to client + */ + protected buyPmcItemFromRagfair(sessionId: string, pmcData: IPmcData, fleaOffer: IRagfairOffer, requestOffer: IOfferRequest, output: IItemEventRouterResponse): IItemEventRouterResponse + { + const buyData: IProcessBuyTradeRequestData = { + Action: "TradingConfirm", + type: "buy_from_ragfair", + tid: "ragfair", + // eslint-disable-next-line @typescript-eslint/naming-convention + item_id: fleaOffer._id, // Store ragfair offerId in buyRequestData.item_id + count: requestOffer.count, + // eslint-disable-next-line @typescript-eslint/naming-convention + scheme_id: 0, + // eslint-disable-next-line @typescript-eslint/naming-convention + scheme_items: requestOffer.items, + }; + + // buyItem() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist for confirmTrading() to use + this.tradeHelper.buyItem(pmcData, buyData, sessionId, this.ragfairConfig.dynamic.purchasesAreFoundInRaid, output); + if (output.warnings.length > 0) + { + return output; + } + + // Remove/lower stack count of item purchased from flea offer + this.ragfairServer.removeOfferStack(fleaOffer._id, requestOffer.count); + + return output; + } + + /** + * Does Player have necessary trader loyalty to purchase flea offer + * @param sellerIsTrader is seller trader + * @param fleaOffer FLea offer being bought + * @param pmcData Player profile + * @returns True if player can buy offer + */ + protected playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer: IRagfairOffer, pmcData: IPmcData): boolean + { + return fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel; + } + /** Handle SellAllFromSavage event */ public sellScavItemsToFence( pmcData: IPmcData, @@ -258,30 +330,4 @@ export class TradeController return totalPrice; } - - /** Buy item */ - protected confirmTradingInternal( - pmcData: IPmcData, - body: IProcessBaseTradeRequestData, - sessionID: string, - foundInRaid = false, - upd: Upd = null, - ): IItemEventRouterResponse - { - // buying - if (body.type === "buy_from_trader") - { - const buyData = body; - return this.tradeHelper.buyItem(pmcData, buyData, sessionID, foundInRaid, upd); - } - - // selling - if (body.type === "sell_to_trader") - { - const sellData = body; - return this.tradeHelper.sellItem(pmcData, pmcData, sellData, sessionID); - } - - return null; - } } \ No newline at end of file diff --git a/project/src/helpers/TradeHelper.ts b/project/src/helpers/TradeHelper.ts index 89f02e04..52c8af94 100644 --- a/project/src/helpers/TradeHelper.ts +++ b/project/src/helpers/TradeHelper.ts @@ -56,19 +56,17 @@ export class TradeHelper * @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 - * @returns + * @param output IItemEventRouterResponse + * @returns IItemEventRouterResponse */ public buyItem( pmcData: IPmcData, buyRequestData: IProcessBuyTradeRequestData, sessionID: string, foundInRaid: boolean, - upd: Upd, + output: IItemEventRouterResponse, ): IItemEventRouterResponse { - let output = this.eventOutputHolder.getOutput(sessionID); - let offerItems: Item[] = []; let buyCallback: { (buyCount: number) }; if (buyRequestData.tid.toLocaleLowerCase() === "ragfair") @@ -102,10 +100,12 @@ export class TradeHelper } /// Pay for item - output = this.paymentService.payMoney(pmcData, buyRequestData, sessionID, output); + this.paymentService.payMoney(pmcData, buyRequestData, sessionID, output); if (output.warnings.length > 0) { - throw new Error(`Transaction failed: ${output.warnings[0].errmsg}`); + this.logger.error(`Flea transaction failed: ${output.warnings[0].errmsg}`); + + return output; } if (assortHasBuyRestrictions) @@ -223,7 +223,7 @@ export class TradeHelper // Construct request const request: IAddItemDirectRequest = { itemWithModsToAdd: this.itemHelper.reparentItemAndChildren(offerItems[0], offerItems), - foundInRaid: this.inventoryConfig.newItemsMarkedFound, + foundInRaid: foundInRaid, callback: buyCallback, useSortingTable: true }; @@ -238,7 +238,6 @@ export class TradeHelper itemsToSendRemaining -= itemCountToSend; } - // TODO - handle traders return output; } diff --git a/project/src/models/eft/trade/IProcessRagfairTradeRequestData.ts b/project/src/models/eft/trade/IProcessRagfairTradeRequestData.ts index 9b6567dc..bb5e07ad 100644 --- a/project/src/models/eft/trade/IProcessRagfairTradeRequestData.ts +++ b/project/src/models/eft/trade/IProcessRagfairTradeRequestData.ts @@ -1,17 +1,17 @@ export interface IProcessRagfairTradeRequestData { Action: string; - offers: Offer[]; + offers: IOfferRequest[]; } -export interface Offer +export interface IOfferRequest { id: string; count: number; - items: Item[]; + items: IItemReqeust[]; } -export interface Item +export interface IItemReqeust { id: string; count: number;