Refactored flea purchase code

This commit is contained in:
Dev 2024-01-16 11:47:40 +00:00
parent 4d1066b51a
commit 818bb7e37a
3 changed files with 132 additions and 87 deletions

View File

@ -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 { Item, Upd } from "@spt-aki/models/eft/common/tables/IItem";
import { ITraderBase } from "@spt-aki/models/eft/common/tables/ITrader"; import { ITraderBase } from "@spt-aki/models/eft/common/tables/ITrader";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; 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 { IProcessBaseTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBaseTradeRequestData";
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData"; 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 { IProcessSellTradeRequestData } from "@spt-aki/models/eft/trade/IProcessSellTradeRequestData";
import { ISellScavItemsToFenceRequestData } from "@spt-aki/models/eft/trade/ISellScavItemsToFenceRequestData"; import { ISellScavItemsToFenceRequestData } from "@spt-aki/models/eft/trade/ISellScavItemsToFenceRequestData";
import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes"; import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes";
@ -60,19 +61,39 @@ export class TradeController
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): 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 = <IProcessBuyTradeRequestData>request;
return this.tradeHelper.buyItem(pmcData, buyData, sessionID, foundInRaid, output);
}
// Selling
if (request.type === "sell_to_trader")
{
const sellData = <IProcessSellTradeRequestData>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 */ /** Handle RagFairBuyOffer event */
public confirmRagfairTrading( public confirmRagfairTrading(
pmcData: IPmcData, pmcData: IPmcData,
body: IProcessRagfairTradeRequestData, request: IProcessRagfairTradeRequestData,
sessionID: string, sessionID: string,
): IItemEventRouterResponse ): 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); const fleaOffer = this.ragfairServer.getOffer(offer.id);
if (!fleaOffer) if (!fleaOffer)
@ -94,59 +115,110 @@ export class TradeController
} }
const sellerIsTrader = fleaOffer.user.memberType === MemberCategory.TRADER; const sellerIsTrader = fleaOffer.user.memberType === MemberCategory.TRADER;
if (sellerIsTrader)
// Skip buying items when player doesn't have needed loyalty
if (sellerIsTrader && fleaOffer.loyaltyLevel > pmcData.TradersInfo[fleaOffer.user.id].loyaltyLevel)
{ {
this.logger.debug( this.buyTraderItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output);
`Unable to buy item: ${ }
fleaOffer.items[0]._tpl else
} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`, {
); this.buyPmcItemFromRagfair(sessionID, pmcData, fleaOffer, offer, output);
continue;
} }
// Log full purchase JSON // Exit loop early if problem found
this.logger.debug(this.jsonUtil.serializeAdvanced(offer, null, 2)); if (output.warnings)
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)
{ {
// Remove the item purchased from flea offer return output;
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 */ /** Handle SellAllFromSavage event */
public sellScavItemsToFence( public sellScavItemsToFence(
pmcData: IPmcData, pmcData: IPmcData,
@ -258,30 +330,4 @@ export class TradeController
return totalPrice; 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 = <IProcessBuyTradeRequestData>body;
return this.tradeHelper.buyItem(pmcData, buyData, sessionID, foundInRaid, upd);
}
// selling
if (body.type === "sell_to_trader")
{
const sellData = <IProcessSellTradeRequestData>body;
return this.tradeHelper.sellItem(pmcData, pmcData, sellData, sessionID);
}
return null;
}
} }

View File

@ -56,19 +56,17 @@ export class TradeHelper
* @param buyRequestData data from client * @param buyRequestData data from client
* @param sessionID Session id * @param sessionID Session id
* @param foundInRaid Should item be found in raid * @param foundInRaid Should item be found in raid
* @param upd optional item details used when buying from flea * @param output IItemEventRouterResponse
* @returns * @returns IItemEventRouterResponse
*/ */
public buyItem( public buyItem(
pmcData: IPmcData, pmcData: IPmcData,
buyRequestData: IProcessBuyTradeRequestData, buyRequestData: IProcessBuyTradeRequestData,
sessionID: string, sessionID: string,
foundInRaid: boolean, foundInRaid: boolean,
upd: Upd, output: IItemEventRouterResponse,
): IItemEventRouterResponse ): IItemEventRouterResponse
{ {
let output = this.eventOutputHolder.getOutput(sessionID);
let offerItems: Item[] = []; let offerItems: Item[] = [];
let buyCallback: { (buyCount: number) }; let buyCallback: { (buyCount: number) };
if (buyRequestData.tid.toLocaleLowerCase() === "ragfair") if (buyRequestData.tid.toLocaleLowerCase() === "ragfair")
@ -102,10 +100,12 @@ export class TradeHelper
} }
/// Pay for item /// Pay for item
output = this.paymentService.payMoney(pmcData, buyRequestData, sessionID, output); this.paymentService.payMoney(pmcData, buyRequestData, sessionID, output);
if (output.warnings.length > 0) 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) if (assortHasBuyRestrictions)
@ -223,7 +223,7 @@ export class TradeHelper
// Construct request // Construct request
const request: IAddItemDirectRequest = { const request: IAddItemDirectRequest = {
itemWithModsToAdd: this.itemHelper.reparentItemAndChildren(offerItems[0], offerItems), itemWithModsToAdd: this.itemHelper.reparentItemAndChildren(offerItems[0], offerItems),
foundInRaid: this.inventoryConfig.newItemsMarkedFound, foundInRaid: foundInRaid,
callback: buyCallback, callback: buyCallback,
useSortingTable: true useSortingTable: true
}; };
@ -238,7 +238,6 @@ export class TradeHelper
itemsToSendRemaining -= itemCountToSend; itemsToSendRemaining -= itemCountToSend;
} }
// TODO - handle traders
return output; return output;
} }

View File

@ -1,17 +1,17 @@
export interface IProcessRagfairTradeRequestData export interface IProcessRagfairTradeRequestData
{ {
Action: string; Action: string;
offers: Offer[]; offers: IOfferRequest[];
} }
export interface Offer export interface IOfferRequest
{ {
id: string; id: string;
count: number; count: number;
items: Item[]; items: IItemReqeust[];
} }
export interface Item export interface IItemReqeust
{ {
id: string; id: string;
count: number; count: number;