From a5450c6552af419837a5bf9c8f64970a58980f47 Mon Sep 17 00:00:00 2001 From: Dev Date: Sun, 25 Feb 2024 11:45:34 +0000 Subject: [PATCH] Moved `addItemWithChildrenToEquipmentSlot()` from `BotWeaponGeneratorHelper` to `botGeneratorHelper` --- project/src/generators/BotLootGenerator.ts | 6 +- project/src/generators/BotWeaponGenerator.ts | 2 +- project/src/generators/PlayerScavGenerator.ts | 4 +- .../ExternalInventoryMagGen.ts | 4 +- project/src/helpers/BotGeneratorHelper.ts | 200 ++++++++++++++++- .../src/helpers/BotWeaponGeneratorHelper.ts | 211 +----------------- 6 files changed, 216 insertions(+), 211 deletions(-) diff --git a/project/src/generators/BotLootGenerator.ts b/project/src/generators/BotLootGenerator.ts index 2bb68a0b..bc5bab51 100644 --- a/project/src/generators/BotLootGenerator.ts +++ b/project/src/generators/BotLootGenerator.ts @@ -3,7 +3,6 @@ import { inject, injectable } from "tsyringe"; import { BotWeaponGenerator } from "@spt-aki/generators/BotWeaponGenerator"; import { BotGeneratorHelper } from "@spt-aki/helpers/BotGeneratorHelper"; import { BotHelper } from "@spt-aki/helpers/BotHelper"; -import { BotWeaponGeneratorHelper } from "@spt-aki/helpers/BotWeaponGeneratorHelper"; import { HandbookHelper } from "@spt-aki/helpers/HandbookHelper"; import { InventoryHelper } from "@spt-aki/helpers/InventoryHelper"; import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; @@ -46,7 +45,6 @@ export class BotLootGenerator @inject("HandbookHelper") protected handbookHelper: HandbookHelper, @inject("BotGeneratorHelper") protected botGeneratorHelper: BotGeneratorHelper, @inject("BotWeaponGenerator") protected botWeaponGenerator: BotWeaponGenerator, - @inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper, @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper, @inject("BotHelper") protected botHelper: BotHelper, @inject("BotLootCacheService") protected botLootCacheService: BotLootCacheService, @@ -442,7 +440,7 @@ export class BotLootGenerator this.addRequiredChildItemsToParent(itemToAddTemplate, itemWithChildrenToAdd, isPmc); // Attempt to add item to container(s) - const itemAddedResult = this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot( + const itemAddedResult = this.botGeneratorHelper.addItemWithChildrenToEquipmentSlot( equipmentSlots, newRootItemId, itemToAddTemplate._id, @@ -597,7 +595,7 @@ export class BotLootGenerator isPmc, botLevel, ); - const result = this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot( + const result = this.botGeneratorHelper.addItemWithChildrenToEquipmentSlot( [equipmentSlot], generatedWeapon.weapon[0]._id, generatedWeapon.weapon[0]._tpl, diff --git a/project/src/generators/BotWeaponGenerator.ts b/project/src/generators/BotWeaponGenerator.ts index 92e58934..4ebf4556 100644 --- a/project/src/generators/BotWeaponGenerator.ts +++ b/project/src/generators/BotWeaponGenerator.ts @@ -514,7 +514,7 @@ export class BotWeaponGenerator for (let i = 0; i < stackCount; i++) { const id = this.hashUtil.generate(); - this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot( + this.botGeneratorHelper.addItemWithChildrenToEquipmentSlot( [EquipmentSlots.SECURED_CONTAINER], id, ammoTpl, diff --git a/project/src/generators/PlayerScavGenerator.ts b/project/src/generators/PlayerScavGenerator.ts index 4ccd3c35..14455db6 100644 --- a/project/src/generators/PlayerScavGenerator.ts +++ b/project/src/generators/PlayerScavGenerator.ts @@ -3,7 +3,6 @@ import { inject, injectable } from "tsyringe"; import { BotGenerator } from "@spt-aki/generators/BotGenerator"; import { BotGeneratorHelper } from "@spt-aki/helpers/BotGeneratorHelper"; import { BotHelper } from "@spt-aki/helpers/BotHelper"; -import { BotWeaponGeneratorHelper } from "@spt-aki/helpers/BotWeaponGeneratorHelper"; import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper"; import { IPmcData } from "@spt-aki/models/eft/common/IPmcData"; @@ -39,7 +38,6 @@ export class PlayerScavGenerator @inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("HashUtil") protected hashUtil: HashUtil, @inject("ItemHelper") protected itemHelper: ItemHelper, - @inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper, @inject("BotGeneratorHelper") protected botGeneratorHelper: BotGeneratorHelper, @inject("SaveServer") protected saveServer: SaveServer, @inject("ProfileHelper") protected profileHelper: ProfileHelper, @@ -171,7 +169,7 @@ export class PlayerScavGenerator ...this.botGeneratorHelper.generateExtraPropertiesForItem(itemTemplate), }]; - const result = this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot( + const result = this.botGeneratorHelper.addItemWithChildrenToEquipmentSlot( containersToAddTo, itemsToAdd[0]._id, itemTemplate._id, diff --git a/project/src/generators/weapongen/implementations/ExternalInventoryMagGen.ts b/project/src/generators/weapongen/implementations/ExternalInventoryMagGen.ts index 2aaf1c1a..e5179a19 100644 --- a/project/src/generators/weapongen/implementations/ExternalInventoryMagGen.ts +++ b/project/src/generators/weapongen/implementations/ExternalInventoryMagGen.ts @@ -2,6 +2,7 @@ import { inject, injectable } from "tsyringe"; import { IInventoryMagGen } from "@spt-aki/generators/weapongen/IInventoryMagGen"; import { InventoryMagGen } from "@spt-aki/generators/weapongen/InventoryMagGen"; +import { BotGeneratorHelper } from "@spt-aki/helpers/BotGeneratorHelper"; import { BotWeaponGeneratorHelper } from "@spt-aki/helpers/BotWeaponGeneratorHelper"; import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; @@ -19,6 +20,7 @@ export class ExternalInventoryMagGen implements IInventoryMagGen @inject("ItemHelper") protected itemHelper: ItemHelper, @inject("LocalisationService") protected localisationService: LocalisationService, @inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper, + @inject("BotGeneratorHelper") protected botGeneratorHelper: BotGeneratorHelper, @inject("RandomUtil") protected randomUtil: RandomUtil, ) {} @@ -56,7 +58,7 @@ export class ExternalInventoryMagGen implements IInventoryMagGen magTemplate, ); - const fitsIntoInventory = this.botWeaponGeneratorHelper.addItemWithChildrenToEquipmentSlot( + const fitsIntoInventory = this.botGeneratorHelper.addItemWithChildrenToEquipmentSlot( [EquipmentSlots.TACTICAL_VEST, EquipmentSlots.POCKETS], magazineWithAmmo[0]._id, magazineTpl, diff --git a/project/src/helpers/BotGeneratorHelper.ts b/project/src/helpers/BotGeneratorHelper.ts index 4fc199be..7920f9fd 100644 --- a/project/src/helpers/BotGeneratorHelper.ts +++ b/project/src/helpers/BotGeneratorHelper.ts @@ -2,13 +2,17 @@ import { inject, injectable } from "tsyringe"; import { ApplicationContext } from "@spt-aki/context/ApplicationContext"; import { ContextVariableType } from "@spt-aki/context/ContextVariableType"; +import { ContainerHelper } from "@spt-aki/helpers/ContainerHelper"; import { DurabilityLimitsHelper } from "@spt-aki/helpers/DurabilityLimitsHelper"; +import { InventoryHelper } from "@spt-aki/helpers/InventoryHelper"; import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; +import { Inventory } from "@spt-aki/models/eft/common/tables/IBotBase"; import { Item, Repairable, Upd } from "@spt-aki/models/eft/common/tables/IItem"; -import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; +import { Grid, ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; import { IGetRaidConfigurationRequestData } from "@spt-aki/models/eft/match/IGetRaidConfigurationRequestData"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; +import { ItemAddedResult } from "@spt-aki/models/enums/ItemAddedResult"; import { IChooseRandomCompatibleModResult } from "@spt-aki/models/spt/bots/IChooseRandomCompatibleModResult"; import { EquipmentFilters, IBotConfig, IRandomisedResourceValues } from "@spt-aki/models/spt/config/IBotConfig"; import { IPmcConfig } from "@spt-aki/models/spt/config/IPmcConfig"; @@ -30,6 +34,8 @@ export class BotGeneratorHelper @inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DurabilityLimitsHelper") protected durabilityLimitsHelper: DurabilityLimitsHelper, @inject("ItemHelper") protected itemHelper: ItemHelper, + @inject("InventoryHelper") protected inventoryHelper: InventoryHelper, + @inject("ContainerHelper") protected containerHelper: ContainerHelper, @inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("LocalisationService") protected localisationService: LocalisationService, @inject("ConfigServer") protected configServer: ConfigServer, @@ -510,4 +516,196 @@ export class BotGeneratorHelper ? "pmc" : botRole; } + + /** + * Adds an item with all its children into specified equipmentSlots, wherever it fits. + * @param equipmentSlots Slot to add item+children into + * @param rootItemId Root item id to use as mod items parentid + * @param rootItemTplId Root itms tpl id + * @param itemWithChildren Item to add + * @param inventory Inventory to add item+children into + * @returns ItemAddedResult result object + */ + public addItemWithChildrenToEquipmentSlot( + equipmentSlots: string[], + rootItemId: string, + rootItemTplId: string, + itemWithChildren: Item[], + inventory: Inventory, + ): ItemAddedResult + { + /** Track how many containers are unable to be found */ + let missingContainerCount = 0; + for (const equipmentSlotId of equipmentSlots) + { + // Get container to put item into + const container = inventory.items.find((item) => item.slotId === equipmentSlotId); + if (!container) + { + missingContainerCount++; + if (missingContainerCount === equipmentSlots.length) + { + // Bot doesnt have any containers we want to add item to + this.logger.debug( + `Unable to add item: ${itemWithChildren[0]._tpl} to bot as it lacks the following containers: ${ + equipmentSlots.join(",") + }`, + ); + + return ItemAddedResult.NO_CONTAINERS; + } + + // No container of desired type found, skip to next container type + continue; + } + + // Get container details from db + const containerTemplate = this.itemHelper.getItem(container._tpl); + if (!containerTemplate[0]) + { + this.logger.warning(this.localisationService.getText("bot-missing_container_with_tpl", container._tpl)); + + // Bad item, skip + continue; + } + + if (!containerTemplate[1]._props.Grids?.length) + { + // Container has no slots to hold items + continue; + } + + // Get x/y grid size of item + const itemSize = this.inventoryHelper.getItemSize(rootItemTplId, rootItemId, itemWithChildren); + + // Iterate over each grid in the container and look for a big enough space for the item to be placed in + let currentGridCount = 1; + const totalSlotGridCount = containerTemplate[1]._props.Grids.length; + for (const slotGrid of containerTemplate[1]._props.Grids) + { + // Grid is empty, skip + if (slotGrid._props.cellsH === 0 || slotGrid._props.cellsV === 0) + { + continue; + } + + // Can't put item type in grid, skip all grids as we're assuming they have the same rules + if (!this.itemAllowedInContainer(slotGrid, rootItemTplId)) + { + // Only one possible slot and item is incompatible, exit function and inform caller + if (equipmentSlots.length === 1) + { + return ItemAddedResult.INCOMPATIBLE_ITEM; + } + + // Multiple containers, maybe next one allows item, only break out of loop for this containers grids + break; + } + + // Get all root items in found container + const existingContainerItems = inventory.items.filter((item) => + item.parentId === container._id && item.slotId === slotGrid._name + ); + + // Get root items in container we can iterate over to find out what space is free + const containerItemsToCheck = existingContainerItems.filter((x) => x.slotId === slotGrid._name); + for (const item of containerItemsToCheck) + { + // Look for children on items, insert into array if found + // (used later when figuring out how much space weapon takes up) + const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(inventory.items, item._id); + if (itemWithChildren.length > 1) + { + existingContainerItems.splice(existingContainerItems.indexOf(item), 1, ...itemWithChildren); + } + } + + // Get rid of items free/used spots in current grid + const slotGridMap = this.inventoryHelper.getContainerMap( + slotGrid._props.cellsH, + slotGrid._props.cellsV, + existingContainerItems, + container._id, + ); + + // Try to fit item into grid + const findSlotResult = this.containerHelper.findSlotForItem(slotGridMap, itemSize[0], itemSize[1]); + + // Open slot found, add item to inventory + if (findSlotResult.success) + { + const parentItem = itemWithChildren.find((i) => i._id === rootItemId); + + // Set items parent to container id + parentItem.parentId = container._id; + parentItem.slotId = slotGrid._name; + parentItem.location = { + x: findSlotResult.x, + y: findSlotResult.y, + r: findSlotResult.rotation ? 1 : 0, + }; + + inventory.items.push(...itemWithChildren); + + return ItemAddedResult.SUCCESS; + } + + // If we've checked all grids in container and reached this point, there's no space for item + if (currentGridCount >= totalSlotGridCount) + { + return ItemAddedResult.NO_SPACE; + } + currentGridCount++; + + // No space in this grid, move to next container grid and try again + } + } + + return ItemAddedResult.UNKNOWN; + } + + /** + * Is the provided item allowed inside a container + * @param slotGrid Items sub-grid we want to place item inside + * @param itemTpl Item tpl being placed + * @returns True if allowed + */ + protected itemAllowedInContainer(slotGrid: Grid, itemTpl: string): boolean + { + const propFilters = slotGrid._props.filters; + const excludedFilter = propFilters[0]?.ExcludedFilter; + const filter = propFilters[0]?.Filter; + + if (propFilters.length === 0) + { + // no filters, item is fine to add + return true; + } + + // Check if item base type is excluded + if (excludedFilter || filter) + { + const itemDetails = this.itemHelper.getItem(itemTpl)[1]; + + // if item to add is found in exclude filter, not allowed + if (excludedFilter.includes(itemDetails._parent)) + { + return false; + } + + // If Filter array only contains 1 filter and its for basetype 'item', allow it + if (filter.length === 1 && filter.includes(BaseClasses.ITEM)) + { + return true; + } + + // If allowed filter has something in it + filter doesnt have basetype 'item', not allowed + if (filter.length > 0 && !filter.includes(itemDetails._parent)) + { + return false; + } + } + + return true; + } } diff --git a/project/src/helpers/BotWeaponGeneratorHelper.ts b/project/src/helpers/BotWeaponGeneratorHelper.ts index 4b81a923..1e0bfb3c 100644 --- a/project/src/helpers/BotWeaponGeneratorHelper.ts +++ b/project/src/helpers/BotWeaponGeneratorHelper.ts @@ -1,13 +1,12 @@ import { inject, injectable } from "tsyringe"; -import { ContainerHelper } from "@spt-aki/helpers/ContainerHelper"; -import { InventoryHelper } from "@spt-aki/helpers/InventoryHelper"; +import { BotGeneratorHelper } from "@spt-aki/helpers/BotGeneratorHelper"; import { ItemHelper } from "@spt-aki/helpers/ItemHelper"; import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper"; import { Inventory } from "@spt-aki/models/eft/common/tables/IBotBase"; import { GenerationData } from "@spt-aki/models/eft/common/tables/IBotType"; import { Item } from "@spt-aki/models/eft/common/tables/IItem"; -import { Grid, ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; +import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { EquipmentSlots } from "@spt-aki/models/enums/EquipmentSlots"; import { ItemAddedResult } from "@spt-aki/models/enums/ItemAddedResult"; @@ -26,10 +25,9 @@ export class BotWeaponGeneratorHelper @inject("ItemHelper") protected itemHelper: ItemHelper, @inject("RandomUtil") protected randomUtil: RandomUtil, @inject("HashUtil") protected hashUtil: HashUtil, - @inject("InventoryHelper") protected inventoryHelper: InventoryHelper, @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper, + @inject("BotGeneratorHelper") protected botGeneratorHelper: BotGeneratorHelper, @inject("LocalisationService") protected localisationService: LocalisationService, - @inject("ContainerHelper") protected containerHelper: ContainerHelper, ) {} @@ -128,9 +126,13 @@ export class BotWeaponGeneratorHelper for (const ammoItem of ammoItems) { - const result = this.addItemWithChildrenToEquipmentSlot(equipmentSlotsToAddTo, ammoItem._id, ammoItem._tpl, [ - ammoItem, - ], inventory); + const result = this.botGeneratorHelper.addItemWithChildrenToEquipmentSlot( + equipmentSlotsToAddTo, + ammoItem._id, + ammoItem._tpl, + [ammoItem], + inventory, + ); if (result !== ItemAddedResult.SUCCESS) { @@ -154,197 +156,4 @@ export class BotWeaponGeneratorHelper { return weaponTemplate._props.defMagType; } - - /** - * TODO - move into BotGeneratorHelper, this is not the class for it - * Adds an item with all its children into specified equipmentSlots, wherever it fits. - * @param equipmentSlots Slot to add item+children into - * @param rootItemId Root item id to use as mod items parentid - * @param rootItemTplId Root itms tpl id - * @param itemWithChildren Item to add - * @param inventory Inventory to add item+children into - * @returns ItemAddedResult result object - */ - public addItemWithChildrenToEquipmentSlot( - equipmentSlots: string[], - rootItemId: string, - rootItemTplId: string, - itemWithChildren: Item[], - inventory: Inventory, - ): ItemAddedResult - { - /** Track how many containers are unable to be found */ - let missingContainerCount = 0; - for (const equipmentSlotId of equipmentSlots) - { - // Get container to put item into - const container = inventory.items.find((item) => item.slotId === equipmentSlotId); - if (!container) - { - missingContainerCount++; - if (missingContainerCount === equipmentSlots.length) - { - // Bot doesnt have any containers we want to add item to - this.logger.debug( - `Unable to add item: ${itemWithChildren[0]._tpl} to bot as it lacks the following containers: ${ - equipmentSlots.join(",") - }`, - ); - - return ItemAddedResult.NO_CONTAINERS; - } - - // No container of desired type found, skip to next container type - continue; - } - - // Get container details from db - const containerTemplate = this.itemHelper.getItem(container._tpl); - if (!containerTemplate[0]) - { - this.logger.warning(this.localisationService.getText("bot-missing_container_with_tpl", container._tpl)); - - // Bad item, skip - continue; - } - - if (!containerTemplate[1]._props.Grids?.length) - { - // Container has no slots to hold items - continue; - } - - // Get x/y grid size of item - const itemSize = this.inventoryHelper.getItemSize(rootItemTplId, rootItemId, itemWithChildren); - - // Iterate over each grid in the container and look for a big enough space for the item to be placed in - let currentGridCount = 1; - const totalSlotGridCount = containerTemplate[1]._props.Grids.length; - for (const slotGrid of containerTemplate[1]._props.Grids) - { - // Grid is empty, skip - if (slotGrid._props.cellsH === 0 || slotGrid._props.cellsV === 0) - { - continue; - } - - // Can't put item type in grid, skip all grids as we're assuming they have the same rules - if (!this.itemAllowedInContainer(slotGrid, rootItemTplId)) - { - // Only one possible slot and item is incompatible, exit function and inform caller - if (equipmentSlots.length === 1) - { - return ItemAddedResult.INCOMPATIBLE_ITEM; - } - - // Multiple containers, maybe next one allows item, only break out of loop for this containers grids - break; - } - - // Get all root items in found container - const existingContainerItems = inventory.items.filter((item) => - item.parentId === container._id && item.slotId === slotGrid._name - ); - - // Get root items in container we can iterate over to find out what space is free - const containerItemsToCheck = existingContainerItems.filter((x) => x.slotId === slotGrid._name); - for (const item of containerItemsToCheck) - { - // Look for children on items, insert into array if found - // (used later when figuring out how much space weapon takes up) - const itemWithChildren = this.itemHelper.findAndReturnChildrenAsItems(inventory.items, item._id); - if (itemWithChildren.length > 1) - { - existingContainerItems.splice(existingContainerItems.indexOf(item), 1, ...itemWithChildren); - } - } - - // Get rid of items free/used spots in current grid - const slotGridMap = this.inventoryHelper.getContainerMap( - slotGrid._props.cellsH, - slotGrid._props.cellsV, - existingContainerItems, - container._id, - ); - - // Try to fit item into grid - const findSlotResult = this.containerHelper.findSlotForItem(slotGridMap, itemSize[0], itemSize[1]); - - // Open slot found, add item to inventory - if (findSlotResult.success) - { - const parentItem = itemWithChildren.find((i) => i._id === rootItemId); - - // Set items parent to container id - parentItem.parentId = container._id; - parentItem.slotId = slotGrid._name; - parentItem.location = { - x: findSlotResult.x, - y: findSlotResult.y, - r: findSlotResult.rotation ? 1 : 0, - }; - - inventory.items.push(...itemWithChildren); - - return ItemAddedResult.SUCCESS; - } - - // If we've checked all grids in container and reached this point, there's no space for item - if (currentGridCount >= totalSlotGridCount) - { - return ItemAddedResult.NO_SPACE; - } - currentGridCount++; - - // No space in this grid, move to next container grid and try again - } - } - - return ItemAddedResult.UNKNOWN; - } - - /** - * Is the provided item allowed inside a container - * @param slotGrid Items sub-grid we want to place item inside - * @param itemTpl Item tpl being placed - * @returns True if allowed - */ - protected itemAllowedInContainer(slotGrid: Grid, itemTpl: string): boolean - { - const propFilters = slotGrid._props.filters; - const excludedFilter = propFilters[0]?.ExcludedFilter; - const filter = propFilters[0]?.Filter; - - if (propFilters.length === 0) - { - // no filters, item is fine to add - return true; - } - - // Check if item base type is excluded - if (excludedFilter || filter) - { - const itemDetails = this.itemHelper.getItem(itemTpl)[1]; - - // if item to add is found in exclude filter, not allowed - if (excludedFilter.includes(itemDetails._parent)) - { - return false; - } - - // If Filter array only contains 1 filter and its for basetype 'item', allow it - if (filter.length === 1 && filter.includes(BaseClasses.ITEM)) - { - return true; - } - - // If allowed filter has something in it + filter doesnt have basetype 'item', not allowed - if (filter.length > 0 && !filter.includes(itemDetails._parent)) - { - return false; - } - } - - return true; - } }