diff --git a/project/assets/configs/trader.json b/project/assets/configs/trader.json index 2ce405a4..a4bdc060 100644 --- a/project/assets/configs/trader.json +++ b/project/assets/configs/trader.json @@ -125,7 +125,11 @@ }, "preventDuplicateOffersOfCategory": [ "543be5cb4bdc2deb348b4568", - "5485a8684bdc2da71d8b4567" + "5485a8684bdc2da71d8b4567", + "5448f3ac4bdc2dce718b4569", + "5448f39d4bdc2d0a728b4568", + "5448f3a14bdc2d27728b4569", + "5448bc234bdc2d3c308b4569" ], "weaponDurabilityPercentMinMax": { "current": { @@ -180,6 +184,14 @@ "544fb3364bdc2d34748b456a": { "min": 1, "max": 800 + }, + "5448bc234bdc2d3c308b4569": { + "min": 1, + "max": 50 + }, + "5448f3a14bdc2d27728b4569": { + "min": 1, + "max": 25 } }, "itemCategoryRoublePriceLimit": { diff --git a/project/src/services/FenceService.ts b/project/src/services/FenceService.ts index e8f67e3d..2e3175a5 100644 --- a/project/src/services/FenceService.ts +++ b/project/src/services/FenceService.ts @@ -46,9 +46,6 @@ export class FenceService /** Hydrated on initial assort generation as part of generateFenceAssorts() */ protected desiredAssortCounts: IFenceAssortGenerationValues; - /** Items that have a multi-stack */ - protected multiStackItems: Record = {}; - constructor( @inject("WinstonLogger") protected logger: ILogger, @inject("JsonUtil") protected jsonUtil: JsonUtil, @@ -553,9 +550,6 @@ export class FenceService x.parentId === "hideout" && !x.upd?.sptPresetId ); - // Clear cache of multi-stack items - this.multiStackItems = {}; - for (let i = 0; i < assortCount; i++) { const chosenBaseAssortRoot = this.randomUtil.getArrayValue(assortRootItems); @@ -611,15 +605,11 @@ export class FenceService rootItemBeingAdded.upd.StackObjectsCount = this.getSingleItemStackCount(itemDbDetails); // Skip items already in the assort if it exists in the prevent duplicate list - if ( - assorts.items.some((item) => item._tpl === rootItemBeingAdded._tpl) - && this.itemHelper.isOfBaseclasses( - rootItemBeingAdded._tpl, - this.traderConfig.fence.preventDuplicateOffersOfCategory, - ) - ) + const existingItem = assorts.items.find((item) => item._tpl === rootItemBeingAdded._tpl); + if (this.itemShouldBeForceStacked(existingItem, itemDbDetails)) { i--; + continue; } @@ -629,20 +619,8 @@ export class FenceService { this.randomiseItemUpdProperties(itemDbDetails, rootItemBeingAdded); } - else - { - // Already have multi-stack, skip - if (this.multiStackItems[itemDbDetails._id]) - { - i--; - continue; - } - // Flag item as added as multi-stack - this.multiStackItems[itemDbDetails._id] = true; - } - - // Need to add mods to armors so they dont show as red in the trade screen + // Add mods to armors so they dont show as red in the trade screen if (this.itemHelper.itemRequiresSoftInserts(rootItemBeingAdded._tpl)) { this.randomiseArmorModDurability(desiredAssortItemAndChildrenClone, itemDbDetails); @@ -664,6 +642,52 @@ export class FenceService } } + /** + * Should this item be forced into only 1 stack on fence + * @param existingItem Existing item from fence assort + * @param itemDbDetails Item we want to add db details + * @returns True item should be force stacked + */ + protected itemShouldBeForceStacked(existingItem: Item, itemDbDetails: ITemplateItem): boolean + { + // No existing item in assort + if (!existingItem) + { + return false; + } + + // Item type not in config list + if ( + !this.itemHelper.isOfBaseclasses( + itemDbDetails._id, + this.traderConfig.fence.preventDuplicateOffersOfCategory, + ) + ) + { + return false; + } + + // Don't stack armored rigs + if (this.itemHelper.isOfBaseclass(itemDbDetails._id, BaseClasses.VEST) && itemDbDetails._props.Slots.length > 0) + { + return false; + } + + // Don't stack meds (e.g. bandages) with unequal usages remaining + // TODO - Doesnt look beyond the first matching fence item, e.g. bandage A has 1 usage left = not a match, but bandage B has 2 uses, would have matched + if ( + this.itemHelper.isOfBaseclasses(itemDbDetails._id, [BaseClasses.MEDICAL, BaseClasses.MEDKIT]) + && existingItem.upd.MedKit?.HpResource // null medkit object means its brand new/max resource + && (itemDbDetails._props.MaxHpResource !== existingItem.upd.MedKit.HpResource) + ) + { + return false; + } + + // Passed all checks, will be forced stacked + return true; + } + /** * Adjust price of item based on what is left to buy (resource/uses left) * @param barterSchemes All barter scheme for item having price adjusted @@ -987,7 +1011,14 @@ export class FenceService } // Check for override in config, use values if exists - const overrideValues = this.traderConfig.fence.itemStackSizeOverrideMinMax[itemDbDetails._id]; + let overrideValues = this.traderConfig.fence.itemStackSizeOverrideMinMax[itemDbDetails._id]; + if (overrideValues) + { + return this.randomUtil.getInt(overrideValues.min, overrideValues.max); + } + + // Check for parent override + overrideValues = this.traderConfig.fence.itemStackSizeOverrideMinMax[itemDbDetails._parent]; if (overrideValues) { return this.randomUtil.getInt(overrideValues.min, overrideValues.max);