Improve buyItem()
handling of multiple and stackable item purchases
rename `reparentPresets` to `reparentItemAndChildren` and move to `itemHelper`
This commit is contained in:
parent
c2f390d4ac
commit
5005a5160a
@ -975,7 +975,7 @@ export class LocationGenerator
|
||||
{
|
||||
try
|
||||
{
|
||||
children = this.ragfairServerHelper.reparentPresets(defaultPreset._items[0], defaultPreset._items);
|
||||
children = this.itemHelper.reparentItemAndChildren(defaultPreset._items[0], defaultPreset._items);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
@ -1014,7 +1014,7 @@ export class LocationGenerator
|
||||
{
|
||||
if (children?.length > 0)
|
||||
{
|
||||
items = this.ragfairServerHelper.reparentPresets(rootItem, children);
|
||||
items = this.itemHelper.reparentItemAndChildren(rootItem, children);
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
|
@ -203,11 +203,9 @@ export class RagfairOfferGenerator
|
||||
{
|
||||
return currencyCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return this.handbookHelper.inRUB(currencyCount, currencyType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check userId, if its a player, return their pmc _id, otherwise return userId parameter
|
||||
@ -352,13 +350,11 @@ export class RagfairOfferGenerator
|
||||
}
|
||||
|
||||
// Get item + sub-items if preset, otherwise just get item
|
||||
const items: Item[] = isPreset
|
||||
const itemWithChildren: Item[] = isPreset
|
||||
? this.ragfairServerHelper.getPresetItems(assortItem)
|
||||
: [
|
||||
...[assortItem],
|
||||
...this.itemHelper.findAndReturnChildrenByAssort(
|
||||
assortItem._id,
|
||||
this.ragfairAssortGenerator.getAssortItems(),
|
||||
...this.itemHelper.findAndReturnChildrenByAssort(assortItem._id, this.ragfairAssortGenerator.getAssortItems(),
|
||||
),
|
||||
];
|
||||
|
||||
@ -372,7 +368,18 @@ export class RagfairOfferGenerator
|
||||
const assortSingleOfferProcesses = [];
|
||||
for (let index = 0; index < offerCount; index++)
|
||||
{
|
||||
assortSingleOfferProcesses.push(this.createSingleOfferForItem(items, isPreset, itemDetails));
|
||||
delete itemWithChildren[0].parentId;
|
||||
delete itemWithChildren[0].slotId;
|
||||
|
||||
if (!isPreset)
|
||||
{
|
||||
// presets get unique id generated during getPresetItems() earlier
|
||||
itemWithChildren[0]._id = this.hashUtil.generate();
|
||||
}
|
||||
|
||||
delete itemWithChildren[0].parentId;
|
||||
|
||||
assortSingleOfferProcesses.push(this.createSingleOfferForItem(itemWithChildren, isPreset, itemDetails));
|
||||
}
|
||||
|
||||
await Promise.all(assortSingleOfferProcesses);
|
||||
|
@ -1112,7 +1112,7 @@ export class RepeatableQuestGenerator
|
||||
{
|
||||
const rootItem = preset.find(x => x._tpl === tpl);
|
||||
rewardItem.target = rootItem._id; // Target property and root items id must match
|
||||
rewardItem.items = this.ragfairServerHelper.reparentPresets(rootItem, preset);
|
||||
rewardItem.items = this.itemHelper.reparentItemAndChildren(rootItem, preset);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -75,7 +75,7 @@ export class InventoryHelper
|
||||
* @param output Client response object
|
||||
* @returns IItemEventRouterResponse
|
||||
*/
|
||||
public addItemToInventory(sessionId: string, request: IAddItemDirectRequest, pmcData: IPmcData, output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||
public addItemToStash(sessionId: string, request: IAddItemDirectRequest, pmcData: IPmcData, output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||
{
|
||||
const itemWithModsToAddClone = this.jsonUtil.clone(request.itemWithModsToAdd);
|
||||
|
||||
@ -83,7 +83,7 @@ export class InventoryHelper
|
||||
const stashFS2D = this.getStashSlotMap(pmcData, sessionId);
|
||||
const sortingTableFS2D = this.getSortingTableSlotMap(pmcData);
|
||||
|
||||
// Find an empty slot in stash for item being added - adds 'location' property to root item
|
||||
// Find empty slot in stash for item being added - adds 'location' + parentid + slotId properties to root item
|
||||
const errorOutput = this.placeItemInInventory(
|
||||
stashFS2D,
|
||||
sortingTableFS2D,
|
||||
@ -99,25 +99,7 @@ export class InventoryHelper
|
||||
}
|
||||
|
||||
// Apply/remove FiR to item + mods
|
||||
for (const item of itemWithModsToAddClone)
|
||||
{
|
||||
if (!item.upd)
|
||||
{
|
||||
item.upd = {};
|
||||
}
|
||||
|
||||
if (request.foundInRaid)
|
||||
{
|
||||
item.upd.SpawnedInSession = request.foundInRaid;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (delete item.upd.SpawnedInSession)
|
||||
{
|
||||
delete item.upd.SpawnedInSession;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.setFindInRaidStatusForItem(itemWithModsToAddClone, request.foundInRaid);
|
||||
|
||||
// Remove trader properties from root item
|
||||
this.removeTraderRagfairRelatedUpdProperties(itemWithModsToAddClone[0].upd);
|
||||
@ -150,7 +132,37 @@ export class InventoryHelper
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - use addItemDirect()
|
||||
* Set FiR status for an item + its children
|
||||
* @param itemWithChildren An item
|
||||
* @param foundInRaid Item was found in raid
|
||||
*/
|
||||
private setFindInRaidStatusForItem(itemWithChildren: Item[], foundInRaid: boolean)
|
||||
{
|
||||
for (const item of itemWithChildren)
|
||||
{
|
||||
// Ensure item has upd object
|
||||
if (!item.upd)
|
||||
{
|
||||
item.upd = {};
|
||||
}
|
||||
|
||||
if (foundInRaid)
|
||||
{
|
||||
item.upd.SpawnedInSession = foundInRaid;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (delete item.upd.SpawnedInSession)
|
||||
{
|
||||
delete item.upd.SpawnedInSession;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - use addItemToStash()
|
||||
*
|
||||
* BUG: Passing the same item multiple times with a count of 1 will cause multiples of that item to be added (e.g. x3 separate objects of tar cola with count of 1 = 9 tarcolas being added to inventory)
|
||||
* @param pmcData Profile to add items to
|
||||
@ -495,7 +507,11 @@ export class InventoryHelper
|
||||
useSortingTable: boolean,
|
||||
output: IItemEventRouterResponse): IItemEventRouterResponse
|
||||
{
|
||||
const itemSize = this.getItemSize(itemWithChildren[0]._tpl, itemWithChildren[0]._id, itemWithChildren);
|
||||
// Get x/y size of item
|
||||
const rootItem = itemWithChildren[0];
|
||||
const itemSize = this.getItemSize(rootItem._tpl, rootItem._id, itemWithChildren);
|
||||
|
||||
// Look for a place to slot item into
|
||||
const findSlotResult = this.containerHelper.findSlotForItem(stashFS2D, itemSize[0], itemSize[1]);
|
||||
|
||||
if (findSlotResult.success)
|
||||
@ -518,7 +534,9 @@ export class InventoryHelper
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
const errorText = typeof err === "string" ? ` -> ${err}` : "";
|
||||
const errorText = (typeof err === "string")
|
||||
? ` -> ${err}`
|
||||
: "";
|
||||
this.logger.error(this.localisationService.getText("inventory-fill_container_failed", errorText));
|
||||
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
@ -527,8 +545,9 @@ export class InventoryHelper
|
||||
);
|
||||
}
|
||||
// Store details for object, incuding container item will be placed in
|
||||
itemWithChildren[0].parentId = playerInventory.stash;
|
||||
itemWithChildren[0].location = {
|
||||
rootItem.parentId = playerInventory.stash;
|
||||
rootItem.slotId = "hideout";
|
||||
rootItem.location = {
|
||||
x: findSlotResult.x,
|
||||
y: findSlotResult.y,
|
||||
r: findSlotResult.rotation ? 1 : 0,
|
||||
@ -774,7 +793,8 @@ export class InventoryHelper
|
||||
protected splitStackIntoSmallerStacks(assortItems: Item[], requestItem: AddItem, result: IAddItemTempObject[]): void
|
||||
{
|
||||
for (const item of assortItems)
|
||||
{// Iterated item matches root item
|
||||
{
|
||||
// Iterated item matches root item
|
||||
if (item._id === requestItem.item_id)
|
||||
{
|
||||
// Get item details from db
|
||||
|
@ -1331,6 +1331,79 @@ export class ItemHelper
|
||||
{
|
||||
return ["front_plate", "back_plate", "side_plate", "left_side_plate", "right_side_plate"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new unique ids for child items while preserving hierarchy
|
||||
* @param rootItem Base/primary item
|
||||
* @param itemWithChildren Primary item + children of primary item
|
||||
* @returns Item array with updated IDs
|
||||
*/
|
||||
public reparentItemAndChildren(rootItem: Item, itemWithChildren: Item[]): Item[]
|
||||
{
|
||||
const oldRootId = itemWithChildren[0]._id;
|
||||
const idMappings = {};
|
||||
|
||||
idMappings[oldRootId] = rootItem._id;
|
||||
|
||||
for (const mod of itemWithChildren)
|
||||
{
|
||||
if (idMappings[mod._id] === undefined)
|
||||
{
|
||||
idMappings[mod._id] = this.hashUtil.generate();
|
||||
}
|
||||
|
||||
// Has parentId + no remapping exists for its parent
|
||||
if (mod.parentId !== undefined && idMappings[mod.parentId] === undefined)
|
||||
{
|
||||
// Make remapping for items parentId
|
||||
idMappings[mod.parentId] = this.hashUtil.generate();
|
||||
}
|
||||
|
||||
mod._id = idMappings[mod._id];
|
||||
|
||||
if (mod.parentId !== undefined)
|
||||
{
|
||||
mod.parentId = idMappings[mod.parentId];
|
||||
}
|
||||
}
|
||||
|
||||
// Force item's details into first location of presetItems
|
||||
if (itemWithChildren[0]._tpl !== rootItem._tpl)
|
||||
{
|
||||
this.logger.warning(`Reassigning root item from ${itemWithChildren[0]._tpl} to ${rootItem._tpl}`);
|
||||
}
|
||||
|
||||
itemWithChildren[0] = rootItem;
|
||||
|
||||
return itemWithChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a root items _id property value to be unique
|
||||
* @param itemWithChildren Item to update root items _id property
|
||||
* @param newId Optional: new id to use
|
||||
*/
|
||||
public remapRootItemId(itemWithChildren: Item[], newId = this.hashUtil.generate()): void
|
||||
{
|
||||
const rootItemExistingId = itemWithChildren[0]._id;
|
||||
|
||||
for (const item of itemWithChildren)
|
||||
{
|
||||
// Root, update id
|
||||
if (item._id === rootItemExistingId)
|
||||
{
|
||||
item._id = newId;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Child with parent of root, update
|
||||
if (item.parentId === rootItemExistingId)
|
||||
{
|
||||
item.parentId = newId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ItemHelper
|
||||
|
@ -323,7 +323,7 @@ export class QuestHelper
|
||||
items.push(this.jsonUtil.clone(mod));
|
||||
}
|
||||
|
||||
rewardItems = rewardItems.concat(this.ragfairServerHelper.reparentPresets(target, items));
|
||||
rewardItems = rewardItems.concat(this.itemHelper.reparentItemAndChildren(target, items));
|
||||
}
|
||||
|
||||
return rewardItems;
|
||||
|
@ -272,7 +272,7 @@ export class RagfairServerHelper
|
||||
public getPresetItems(item: Item): Item[]
|
||||
{
|
||||
const preset = this.jsonUtil.clone(this.databaseServer.getTables().globals.ItemPresets[item._id]._items);
|
||||
return this.reparentPresets(item, preset);
|
||||
return this.itemHelper.reparentItemAndChildren(item, preset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -290,56 +290,10 @@ export class RagfairServerHelper
|
||||
const presetItems = this.jsonUtil.clone(
|
||||
this.databaseServer.getTables().globals.ItemPresets[itemId]._items,
|
||||
);
|
||||
presets.push(this.reparentPresets(item, presetItems));
|
||||
presets.push(this.itemHelper.reparentItemAndChildren(item, presetItems));
|
||||
}
|
||||
}
|
||||
|
||||
return presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new unique ids for child items while preserving hierarchy
|
||||
* @param rootItem Base/primary item of preset
|
||||
* @param preset Primary item + children of primary item
|
||||
* @returns Item array with new IDs
|
||||
*/
|
||||
public reparentPresets(rootItem: Item, preset: Item[]): Item[]
|
||||
{
|
||||
const oldRootId = preset[0]._id;
|
||||
const idMappings = {};
|
||||
|
||||
idMappings[oldRootId] = rootItem._id;
|
||||
|
||||
for (const mod of preset)
|
||||
{
|
||||
if (idMappings[mod._id] === undefined)
|
||||
{
|
||||
idMappings[mod._id] = this.hashUtil.generate();
|
||||
}
|
||||
|
||||
// Has parentId + no remapping exists for its parent
|
||||
if (mod.parentId !== undefined && idMappings[mod.parentId] === undefined)
|
||||
{
|
||||
// Make remapping for items parentId
|
||||
idMappings[mod.parentId] = this.hashUtil.generate();
|
||||
}
|
||||
|
||||
mod._id = idMappings[mod._id];
|
||||
|
||||
if (mod.parentId !== undefined)
|
||||
{
|
||||
mod.parentId = idMappings[mod.parentId];
|
||||
}
|
||||
}
|
||||
|
||||
// Force item's details into first location of presetItems
|
||||
if (preset[0]._tpl !== rootItem._tpl)
|
||||
{
|
||||
this.logger.warning(`Reassigning root item from ${preset[0]._tpl} to ${rootItem._tpl}`);
|
||||
}
|
||||
|
||||
preset[0] = rootItem;
|
||||
|
||||
return preset;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessB
|
||||
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 { IInventoryConfig } from "@spt-aki/models/spt/config/IInventoryConfig";
|
||||
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";
|
||||
@ -25,6 +26,7 @@ import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
||||
export class TradeHelper
|
||||
{
|
||||
protected traderConfig: ITraderConfig;
|
||||
protected inventoryConfig: IInventoryConfig;
|
||||
|
||||
constructor(
|
||||
@inject("WinstonLogger") protected logger: ILogger,
|
||||
@ -41,6 +43,7 @@ export class TradeHelper
|
||||
)
|
||||
{
|
||||
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
|
||||
this.inventoryConfig = this.configServer.getConfig(ConfigTypes.INVENTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,17 +131,41 @@ export class TradeHelper
|
||||
|
||||
if (buyRequestData.tid.toLocaleLowerCase() === "ragfair")
|
||||
{
|
||||
// Get raw offer from ragfair, clone to prevent altering offer itself
|
||||
const allOffers = this.ragfairServer.getOffers();
|
||||
const offerWithItem = allOffers.find((x) => x._id === buyRequestData.item_id);
|
||||
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)
|
||||
{
|
||||
// Handle edge case when remaining items to send < max stack size
|
||||
const itemCountToSend = Math.min(itemMaxStackSize, itemsToSendRemaining);
|
||||
offerItems[0].upd.StackObjectsCount = itemCountToSend;
|
||||
|
||||
// Prevent any collisions
|
||||
this.itemHelper.remapRootItemId(offerItems);
|
||||
|
||||
// Construct request
|
||||
const request: IAddItemDirectRequest = {
|
||||
itemWithModsToAdd: offerWithItem.items,
|
||||
foundInRaid: true,
|
||||
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;
|
||||
}
|
||||
|
||||
return this.inventoryHelper.addItemToInventory(sessionID, request, pmcData, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
// TODO - handle traders
|
||||
|
Loading…
x
Reference in New Issue
Block a user