diff --git a/project/src/controllers/HideoutController.ts b/project/src/controllers/HideoutController.ts index 21de1d99..fcd2ad89 100644 --- a/project/src/controllers/HideoutController.ts +++ b/project/src/controllers/HideoutController.ts @@ -30,6 +30,7 @@ import { IAddItemDirectRequest } from "@spt/models/eft/inventory/IAddItemDirectR import { IAddItemsDirectRequest } from "@spt/models/eft/inventory/IAddItemsDirectRequest"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { BackendErrorCodes } from "@spt/models/enums/BackendErrorCodes"; +import { BaseClasses } from "@spt/models/enums/BaseClasses"; import { BonusType } from "@spt/models/enums/BonusType"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { HideoutAreas } from "@spt/models/enums/HideoutAreas"; @@ -874,10 +875,10 @@ export class HideoutController { // Reward is weapon/armor preset, handle differently compared to 'normal' items const rewardIsPreset = this.presetHelper.hasPreset(recipe.endProduct); if (rewardIsPreset) { - const preset = this.presetHelper.getDefaultPreset(recipe.endProduct); + const defaultPreset = this.presetHelper.getDefaultPreset(recipe.endProduct); // Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory - const presetAndMods: Item[] = this.itemHelper.replaceIDs(preset._items); + const presetAndMods: Item[] = this.itemHelper.replaceIDs(defaultPreset._items); this.itemHelper.remapRootItemId(presetAndMods); @@ -1384,15 +1385,49 @@ export class HideoutController { // Pick random rewards until we have exhausted the sacrificed items budget let totalCost = 0; let itemsRewardedCount = 0; + let failedAttempts = 0; while (totalCost < rewardBudget && rewardItemTplPool.length > 0 && itemsRewardedCount < 5) { + if (failedAttempts > 5) { + this.logger.warning(`Exiting reward generation after ${failedAttempts} failed attempts`); + + break; + } + + // Choose a random tpl from pool const randomItemTplFromPool = this.randomUtil.getArrayValue(rewardItemTplPool); + + // Is weapon/armor, handle differently + if ( + this.itemHelper.armorItemHasRemovableOrSoftInsertSlots(randomItemTplFromPool) || + this.itemHelper.isOfBaseclass(randomItemTplFromPool, BaseClasses.WEAPON) + ) { + const defaultPreset = this.presetHelper.getDefaultPreset(randomItemTplFromPool); + if (!defaultPreset) { + this.logger.warning(`Reward tpl: ${randomItemTplFromPool} lacks a default preset, skipping reward`); + failedAttempts++; + + continue; + } + + // Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory + const presetAndMods: Item[] = this.itemHelper.replaceIDs(defaultPreset._items); + + this.itemHelper.remapRootItemId(presetAndMods); + + rewards.push(presetAndMods); + } + + // Some items can have variable stack size, e.g. ammo + const stackSize = this.getRewardStackSize(randomItemTplFromPool); + + // Not a weapon/armor, standard single item const rewardItem: Item = { _id: this.hashUtil.generate(), _tpl: randomItemTplFromPool, parentId: cultistCircleStashId, slotId: HideoutController.circleOfCultistSlotId, upd: { - StackObjectsCount: 1, + StackObjectsCount: stackSize, SpawnedInSession: true, }, }; @@ -1407,6 +1442,15 @@ export class HideoutController { return rewards; } + protected getRewardStackSize(randomItemTplFromPool: string) { + if (this.itemHelper.isOfBaseclass(randomItemTplFromPool, BaseClasses.AMMO)) { + const ammoTemplate = this.itemHelper.getItem(randomItemTplFromPool)[1]; + return this.itemHelper.getRandomisedAmmoStackSize(ammoTemplate); + } + + return 1; + } + /** * Get a pool of tpl IDs of items the player needs to complete hideout crafts/upgrade areas * @param sessionId Session id diff --git a/project/src/generators/BotLootGenerator.ts b/project/src/generators/BotLootGenerator.ts index f71eaa82..3e1f5783 100644 --- a/project/src/generators/BotLootGenerator.ts +++ b/project/src/generators/BotLootGenerator.ts @@ -760,14 +760,7 @@ export class BotLootGenerator { * @param ammoItem Ammo item to randomise */ protected randomiseAmmoStackSize(isPmc: boolean, itemTemplate: ITemplateItem, ammoItem: Item): void { - const randomSize = - itemTemplate._props.StackMaxSize === 1 - ? 1 - : this.randomUtil.getInt( - itemTemplate._props.StackMinRandom, - Math.min(itemTemplate._props.StackMaxRandom, 60), - ); - + const randomSize = this.itemHelper.getRandomisedAmmoStackSize(itemTemplate); this.itemHelper.addUpdObjectToItem(ammoItem); ammoItem.upd.StackObjectsCount = randomSize; diff --git a/project/src/helpers/ItemHelper.ts b/project/src/helpers/ItemHelper.ts index 39930411..3b9f15c2 100644 --- a/project/src/helpers/ItemHelper.ts +++ b/project/src/helpers/ItemHelper.ts @@ -1723,9 +1723,28 @@ export class ItemHelper { return false; } + /** + * Return all tpls from Money enum + * @returns string tpls + */ public getMoneyTpls(): string[] { return Object.values(Money); } + + /** + * Get a randomsied stack size for the passed in ammo + * @param ammoItemTemplate Ammo to get stack size for + * @param maxLimit default: Limit to 60 to prevent crazy values when players use stack increase mods + * @returns number + */ + public getRandomisedAmmoStackSize(ammoItemTemplate: ITemplateItem, maxLimit = 60): number { + return ammoItemTemplate._props.StackMaxSize === 1 + ? 1 + : this.randomUtil.getInt( + ammoItemTemplate._props.StackMinRandom, + Math.min(ammoItemTemplate._props.StackMaxRandom, maxLimit), + ); + } } namespace ItemHelper { diff --git a/project/src/helpers/PresetHelper.ts b/project/src/helpers/PresetHelper.ts index db485bcc..265f1f62 100644 --- a/project/src/helpers/PresetHelper.ts +++ b/project/src/helpers/PresetHelper.ts @@ -116,9 +116,9 @@ export class PresetHelper { } /** - * Get the default preset for passed in item id - * @param templateId Item id to get preset for - * @returns Null if no default preset, otherwise IPreset + * Get a cloned default preset for passed in item tpl + * @param templateId Item tpl to get preset for + * @returns undefined if no default preset, otherwise IPreset */ public getDefaultPreset(templateId: string): IPreset | undefined { if (!this.hasPreset(templateId)) {