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 { 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 = <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 */
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 = <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 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;
}

View File

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