From 1d9ff8ace874c67eb82139611cd68b1da7ccc82c Mon Sep 17 00:00:00 2001 From: Dev Date: Sun, 14 Jan 2024 22:30:05 +0000 Subject: [PATCH] Improve `buyItem()` to also support putting fence purchases through `addItemToStash()` --- project/src/helpers/InventoryHelper.ts | 6 +- project/src/helpers/TradeHelper.ts | 165 ++++++++++++++++--------- 2 files changed, 113 insertions(+), 58 deletions(-) diff --git a/project/src/helpers/InventoryHelper.ts b/project/src/helpers/InventoryHelper.ts index 4ce2a53f..d73305a9 100644 --- a/project/src/helpers/InventoryHelper.ts +++ b/project/src/helpers/InventoryHelper.ts @@ -79,7 +79,7 @@ export class InventoryHelper { const itemWithModsToAddClone = this.jsonUtil.clone(request.itemWithModsToAdd); - // get stash layouts ready for use + // Get stash layouts ready for use const stashFS2D = this.getStashSlotMap(pmcData, sessionId); const sortingTableFS2D = this.getSortingTableSlotMap(pmcData); @@ -122,11 +122,11 @@ export class InventoryHelper return this.httpResponse.appendErrorToOutput(output, message); } - // Add item + mods to output + profile inventory + // Add item + mods to output and profile inventory output.profileChanges[sessionId].items.new.push(...itemWithModsToAddClone); pmcData.Inventory.items.push(...itemWithModsToAddClone); - this.logger.debug(`Added item ${itemWithModsToAddClone[0]._tpl} with ${itemWithModsToAddClone.length - 1} mods to inventory`); + this.logger.debug(`Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount} item: ${itemWithModsToAddClone[0]._tpl} with: ${itemWithModsToAddClone.length - 1} mods to inventory`); return output; } diff --git a/project/src/helpers/TradeHelper.ts b/project/src/helpers/TradeHelper.ts index 61600345..32f7e0af 100644 --- a/project/src/helpers/TradeHelper.ts +++ b/project/src/helpers/TradeHelper.ts @@ -18,6 +18,7 @@ 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 { LocalisationService } from "@spt-aki/services/LocalisationService"; import { PaymentService } from "@spt-aki/services/PaymentService"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil"; @@ -36,6 +37,7 @@ export class TradeHelper @inject("ItemHelper") protected itemHelper: ItemHelper, @inject("PaymentService") protected paymentService: PaymentService, @inject("FenceService") protected fenceService: FenceService, + @inject("LocalisationService") protected localisationService: LocalisationService, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("InventoryHelper") protected inventoryHelper: InventoryHelper, @inject("RagfairServer") protected ragfairServer: RagfairServer, @@ -77,20 +79,9 @@ export class TradeHelper const callback = () => { // Update assort/flea item values - let itemPurchased: Item; - const isRagfair = buyRequestData.tid.toLocaleLowerCase() === "ragfair"; - if (isRagfair) - { - const allOffers = this.ragfairServer.getOffers(); - // We store ragfair offerid in buyRequestData.item_id - const offerWithItem = allOffers.find((x) => x._id === buyRequestData.item_id); - itemPurchased = offerWithItem.items[0]; - } - else - { - const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items; - itemPurchased = traderAssorts.find((x) => x._id === buyRequestData.item_id); - } + const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items; + const itemPurchased = traderAssorts.find((x) => x._id === buyRequestData.item_id); + // Ensure purchase does not exceed trader item limit const hasBuyRestrictions = this.itemHelper.hasBuyRestrictions(itemPurchased); @@ -100,10 +91,7 @@ export class TradeHelper } // Decrement trader item count - if (!isRagfair) - { - itemPurchased.upd.StackObjectsCount -= buyRequestData.count; - } + itemPurchased.upd.StackObjectsCount -= buyRequestData.count; if (this.traderConfig.persistPurchaseDataInProfile && hasBuyRestrictions) { @@ -117,61 +105,128 @@ export class TradeHelper 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); - } - else if (hasBuyRestrictions) + if (hasBuyRestrictions) { // Increment non-fence trader item buy count this.incrementAssortBuyCount(itemPurchased, buyRequestData.count); } }; + // Handle normal traders old way...for now + if (buyRequestData.tid.toLocaleLowerCase() !== "ragfair" && buyRequestData.tid.toLocaleLowerCase() !== Traders.FENCE) + { + return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, foundInRaid, upd); + } + + let offerItems: Item[] = []; + let buyCallback; if (buyRequestData.tid.toLocaleLowerCase() === "ragfair") { + buyCallback = () => + { + const allOffers = this.ragfairServer.getOffers(); + + // We store ragfair offerid in buyRequestData.item_id + const offerWithItem = allOffers.find((x) => x._id === buyRequestData.item_id); + const itemPurchased = offerWithItem.items[0]; + + // 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 (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 (hasBuyRestrictions) + { + // Increment non-fence trader item buy count + this.incrementAssortBuyCount(itemPurchased, buyRequestData.count); + } + }; + // Get raw offer from ragfair, clone to prevent altering offer itself const allOffers = this.ragfairServer.getOffers(); const offerWithItemCloned = this.jsonUtil.clone(allOffers.find((x) => x._id === buyRequestData.item_id)); - const offerItems = offerWithItemCloned.items; - - - // Get item details from db - const itemDbDetails = this.itemHelper.getItem(offerItems[0]._tpl)[1]; - const itemMaxStackSize = itemDbDetails._props.StackMaxSize; - const itemsToSendTotalCount = buyRequestData.count; - let itemsToSendRemaining = itemsToSendTotalCount; - while (itemsToSendRemaining > 0) + offerItems = offerWithItemCloned.items; + } + else if (buyRequestData.tid === Traders.FENCE) + { + buyCallback = () => { - // Handle edge case when remaining items to send < max stack size - const itemCountToSend = Math.min(itemMaxStackSize, itemsToSendRemaining); - offerItems[0].upd.StackObjectsCount = itemCountToSend; + // Update assort/flea item values + const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items; + const itemPurchased = traderAssorts.find((x) => x._id === buyRequestData.item_id); + + // Decrement trader item count + itemPurchased.upd.StackObjectsCount -= buyRequestData.count; + + /// 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}`); + } + + this.fenceService.removeFenceOffer(buyRequestData.item_id); + }; - // Prevent any collisions - this.itemHelper.remapRootItemId(offerItems); - - // Construct request - const request: IAddItemDirectRequest = { - itemWithModsToAdd: this.itemHelper.reparentItemAndChildren(offerItems[0], offerItems), - foundInRaid: this.inventoryConfig.newItemsMarkedFound, - callback: callback, - useSortingTable: true - }; - - this.inventoryHelper.addItemToStash(sessionID, request, pmcData, output); - - // Remove amount of items added to player stash - itemsToSendRemaining -= itemCountToSend; + const fenceItems = this.fenceService.getRawFenceAssorts().items; + const rootItemIndex = fenceItems.findIndex((item) => item._id === buyRequestData.item_id); + if (rootItemIndex === -1) + { + this.logger.debug(`Tried to buy item ${buyRequestData.item_id} from fence that no longer exists`); + const message = this.localisationService.getText("ragfair-offer_no_longer_exists"); + return this.httpResponse.appendErrorToOutput(output, message); } - return output; + offerItems = this.itemHelper.findAndReturnChildrenAsItems(fenceItems, buyRequestData.item_id); } - // TODO - handle traders - // TODO - handle fence + // Get item details from db + const itemDbDetails = this.itemHelper.getItem(offerItems[0]._tpl)[1]; + const itemMaxStackSize = itemDbDetails._props.StackMaxSize; + const itemsToSendTotalCount = buyRequestData.count; + let itemsToSendRemaining = itemsToSendTotalCount; + while (itemsToSendRemaining > 0) + { + // Handle edge case when remaining items to send < max stack size + const itemCountToSend = Math.min(itemMaxStackSize, itemsToSendRemaining); + offerItems[0].upd.StackObjectsCount = itemCountToSend; - return this.inventoryHelper.addItem(pmcData, newReq, output, sessionID, callback, foundInRaid, upd); + // Prevent any collisions + this.itemHelper.remapRootItemId(offerItems); + + // Construct request + const request: IAddItemDirectRequest = { + itemWithModsToAdd: this.itemHelper.reparentItemAndChildren(offerItems[0], offerItems), + foundInRaid: this.inventoryConfig.newItemsMarkedFound, + callback: buyCallback, + useSortingTable: true + }; + + // Add item + children to stash + this.inventoryHelper.addItemToStash(sessionID, request, pmcData, output); + + // Remove amount of items added to player stash + itemsToSendRemaining -= itemCountToSend; + } + + // TODO - handle traders + return output; } /**