2023-03-03 16:23:46 +01:00
import { inject , injectable } from "tsyringe" ;
2023-10-19 19:21:17 +02:00
import { BotWeaponGenerator } from "@spt-aki/generators/BotWeaponGenerator" ;
import { BotGeneratorHelper } from "@spt-aki/helpers/BotGeneratorHelper" ;
import { BotWeaponGeneratorHelper } from "@spt-aki/helpers/BotWeaponGeneratorHelper" ;
import { HandbookHelper } from "@spt-aki/helpers/HandbookHelper" ;
import { ItemHelper } from "@spt-aki/helpers/ItemHelper" ;
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper" ;
import { Inventory as PmcInventory } from "@spt-aki/models/eft/common/tables/IBotBase" ;
import { IBotType , Inventory , ModsChances } from "@spt-aki/models/eft/common/tables/IBotType" ;
import { Item } from "@spt-aki/models/eft/common/tables/IItem" ;
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem" ;
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses" ;
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes" ;
import { EquipmentSlots } from "@spt-aki/models/enums/EquipmentSlots" ;
import { ItemAddedResult } from "@spt-aki/models/enums/ItemAddedResult" ;
import { LootCacheType } from "@spt-aki/models/spt/bots/IBotLootCache" ;
import { IBotConfig } from "@spt-aki/models/spt/config/IBotConfig" ;
import { IPmcConfig } from "@spt-aki/models/spt/config/IPmcConfig" ;
import { ILogger } from "@spt-aki/models/spt/utils/ILogger" ;
import { ConfigServer } from "@spt-aki/servers/ConfigServer" ;
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer" ;
import { BotLootCacheService } from "@spt-aki/services/BotLootCacheService" ;
import { LocalisationService } from "@spt-aki/services/LocalisationService" ;
import { HashUtil } from "@spt-aki/utils/HashUtil" ;
import { RandomUtil } from "@spt-aki/utils/RandomUtil" ;
2023-03-03 16:23:46 +01:00
@injectable ( )
export class BotLootGenerator
{
protected botConfig : IBotConfig ;
2023-10-10 13:03:20 +02:00
protected pmcConfig : IPmcConfig ;
2023-11-16 22:42:06 +01:00
2023-03-03 16:23:46 +01:00
constructor (
@inject ( "WinstonLogger" ) protected logger : ILogger ,
@inject ( "HashUtil" ) protected hashUtil : HashUtil ,
@inject ( "RandomUtil" ) protected randomUtil : RandomUtil ,
@inject ( "ItemHelper" ) protected itemHelper : ItemHelper ,
@inject ( "DatabaseServer" ) protected databaseServer : DatabaseServer ,
@inject ( "HandbookHelper" ) protected handbookHelper : HandbookHelper ,
@inject ( "BotGeneratorHelper" ) protected botGeneratorHelper : BotGeneratorHelper ,
@inject ( "BotWeaponGenerator" ) protected botWeaponGenerator : BotWeaponGenerator ,
@inject ( "BotWeaponGeneratorHelper" ) protected botWeaponGeneratorHelper : BotWeaponGeneratorHelper ,
2023-10-10 13:03:20 +02:00
@inject ( "WeightedRandomHelper" ) protected weightedRandomHelper : WeightedRandomHelper ,
2023-03-03 16:23:46 +01:00
@inject ( "BotLootCacheService" ) protected botLootCacheService : BotLootCacheService ,
@inject ( "LocalisationService" ) protected localisationService : LocalisationService ,
2023-11-16 22:42:06 +01:00
@inject ( "ConfigServer" ) protected configServer : ConfigServer ,
2023-03-03 16:23:46 +01:00
)
{
this . botConfig = this . configServer . getConfig ( ConfigTypes . BOT ) ;
2023-10-10 13:03:20 +02:00
this . pmcConfig = this . configServer . getConfig ( ConfigTypes . PMC ) ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Add loot to bots containers
* @param sessionId Session id
2023-03-17 19:20:16 +01:00
* @param botJsonTemplate Base json db file for the bot having its loot generated
2023-03-03 16:23:46 +01:00
* @param isPmc Will bot be a pmc
* @param botRole Role of bot , e . g . asssult
* @param botInventory Inventory to add loot to
* @param botLevel Level of bot
* /
2023-11-16 22:42:06 +01:00
public generateLoot (
sessionId : string ,
botJsonTemplate : IBotType ,
isPmc : boolean ,
botRole : string ,
botInventory : PmcInventory ,
botLevel : number ,
) : void
2023-03-03 16:23:46 +01:00
{
2023-03-17 19:20:16 +01:00
// Limits on item types to be added as loot
const itemCounts = botJsonTemplate . generation . items ;
2023-11-16 22:42:06 +01:00
2023-10-10 13:03:20 +02:00
const backpackLootCount = this . weightedRandomHelper . getWeightedValue < number > ( itemCounts . backpackLoot . weights ) ;
const pocketLootCount = this . weightedRandomHelper . getWeightedValue < number > ( itemCounts . pocketLoot . weights ) ;
const vestLootCount = this . weightedRandomHelper . getWeightedValue < number > ( itemCounts . vestLoot . weights ) ;
2023-11-16 22:42:06 +01:00
const specialLootItemCount = this . weightedRandomHelper . getWeightedValue < number > (
itemCounts . specialItems . weights ,
) ;
2023-10-10 13:03:20 +02:00
const healingItemCount = this . weightedRandomHelper . getWeightedValue < number > ( itemCounts . healing . weights ) ;
const drugItemCount = this . weightedRandomHelper . getWeightedValue < number > ( itemCounts . drugs . weights ) ;
const stimItemCount = this . weightedRandomHelper . getWeightedValue < number > ( itemCounts . stims . weights ) ;
const grenadeCount = this . weightedRandomHelper . getWeightedValue < number > ( itemCounts . grenades . weights ) ;
2023-03-03 16:23:46 +01:00
2023-07-27 13:15:06 +02:00
// Forced pmc healing loot
2023-10-10 13:03:20 +02:00
if ( isPmc && this . pmcConfig . forceHealingItemsIntoSecure )
2023-07-27 13:15:06 +02:00
{
this . addForcedMedicalItemsToPmcSecure ( botInventory , botRole ) ;
}
2023-10-10 13:03:20 +02:00
const containersBotHasAvailable = this . getAvailableContainersBotCanStoreItemsIn ( botInventory ) ;
2023-03-03 16:23:46 +01:00
// Special items
this . addLootFromPool (
2023-03-17 19:20:16 +01:00
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . SPECIAL , botJsonTemplate ) ,
2023-10-10 13:03:20 +02:00
containersBotHasAvailable ,
2023-03-03 16:23:46 +01:00
specialLootItemCount ,
botInventory ,
2023-11-16 22:42:06 +01:00
botRole ,
) ;
2023-03-03 16:23:46 +01:00
2023-10-12 14:25:13 +02:00
// Healing items / Meds
2023-03-03 16:23:46 +01:00
this . addLootFromPool (
2023-03-17 19:20:16 +01:00
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . HEALING_ITEMS , botJsonTemplate ) ,
2023-10-10 13:03:20 +02:00
containersBotHasAvailable ,
2023-03-03 16:23:46 +01:00
healingItemCount ,
botInventory ,
botRole ,
false ,
0 ,
2023-11-16 22:42:06 +01:00
isPmc ,
) ;
2023-03-03 16:23:46 +01:00
// Drugs
this . addLootFromPool (
2023-03-17 19:20:16 +01:00
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . DRUG_ITEMS , botJsonTemplate ) ,
2023-10-10 13:03:20 +02:00
containersBotHasAvailable ,
2023-03-03 16:23:46 +01:00
drugItemCount ,
botInventory ,
botRole ,
false ,
0 ,
2023-11-16 22:42:06 +01:00
isPmc ,
) ;
2023-03-03 16:23:46 +01:00
// Stims
this . addLootFromPool (
2023-03-17 19:20:16 +01:00
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . STIM_ITEMS , botJsonTemplate ) ,
2023-10-10 13:03:20 +02:00
containersBotHasAvailable ,
2023-03-03 16:23:46 +01:00
stimItemCount ,
botInventory ,
botRole ,
true ,
0 ,
2023-11-16 22:42:06 +01:00
isPmc ,
) ;
2023-03-03 16:23:46 +01:00
// Grenades
this . addLootFromPool (
2023-03-17 19:20:16 +01:00
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . GRENADE_ITEMS , botJsonTemplate ) ,
2023-10-10 13:03:20 +02:00
[ EquipmentSlots . POCKETS , EquipmentSlots . TACTICAL_VEST ] , // Can't use containersBotHasEquipped as we dont want grenades added to backpack
2023-03-03 16:23:46 +01:00
grenadeCount ,
botInventory ,
botRole ,
false ,
0 ,
2023-11-16 22:42:06 +01:00
isPmc ,
) ;
2023-03-03 16:23:46 +01:00
2023-07-24 14:20:17 +02:00
// Backpack - generate loot if they have one
2023-10-10 13:03:20 +02:00
if ( containersBotHasAvailable . includes ( EquipmentSlots . BACKPACK ) )
2023-07-24 14:20:17 +02:00
{
// Add randomly generated weapon to PMC backpacks
2023-10-10 13:03:20 +02:00
if ( isPmc && this . randomUtil . getChance100 ( this . pmcConfig . looseWeaponInBackpackChancePercent ) )
2023-07-24 14:20:17 +02:00
{
2023-11-16 22:42:06 +01:00
this . addLooseWeaponsToInventorySlot (
sessionId ,
botInventory ,
EquipmentSlots . BACKPACK ,
botJsonTemplate . inventory ,
2024-01-09 16:31:56 +01:00
botJsonTemplate . chances . weaponMods ,
2023-11-16 22:42:06 +01:00
botRole ,
isPmc ,
botLevel ,
) ;
2023-07-24 14:20:17 +02:00
}
2023-03-03 16:23:46 +01:00
2023-07-24 14:20:17 +02:00
this . addLootFromPool (
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . BACKPACK , botJsonTemplate ) ,
[ EquipmentSlots . BACKPACK ] ,
2023-10-10 13:03:20 +02:00
backpackLootCount ,
2023-07-24 14:20:17 +02:00
botInventory ,
botRole ,
true ,
2023-10-10 13:03:20 +02:00
this . pmcConfig . maxBackpackLootTotalRub ,
2023-11-16 22:42:06 +01:00
isPmc ,
) ;
2023-07-24 14:20:17 +02:00
}
2023-11-16 22:42:06 +01:00
2023-10-10 13:03:20 +02:00
// TacticalVest - generate loot if they have one
if ( containersBotHasAvailable . includes ( EquipmentSlots . TACTICAL_VEST ) )
{
// Vest
this . addLootFromPool (
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . VEST , botJsonTemplate ) ,
[ EquipmentSlots . TACTICAL_VEST ] ,
vestLootCount ,
botInventory ,
botRole ,
true ,
this . pmcConfig . maxVestLootTotalRub ,
2023-11-16 22:42:06 +01:00
isPmc ,
) ;
2023-10-10 13:03:20 +02:00
}
2023-03-03 16:23:46 +01:00
// Pockets
this . addLootFromPool (
2023-03-17 19:20:16 +01:00
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . POCKET , botJsonTemplate ) ,
2023-03-03 16:23:46 +01:00
[ EquipmentSlots . POCKETS ] ,
pocketLootCount ,
botInventory ,
botRole ,
true ,
2023-10-10 13:03:20 +02:00
this . pmcConfig . maxPocketLootTotalRub ,
2023-11-16 22:42:06 +01:00
isPmc ,
) ;
2024-01-18 13:07:32 +01:00
// Secure
this . addLootFromPool (
this . botLootCacheService . getLootFromCache ( botRole , isPmc , LootCacheType . SECURE , botJsonTemplate ) ,
[ EquipmentSlots . SECURED_CONTAINER ] ,
2024-01-19 20:21:51 +01:00
50 ,
2024-01-18 13:07:32 +01:00
botInventory ,
botRole ,
false ,
- 1 ,
isPmc ,
) ;
2023-03-03 16:23:46 +01:00
}
2023-10-10 13:03:20 +02:00
/ * *
* Get an array of the containers a bot has on them ( pockets / backpack / vest )
* @param botInventory Bot to check
* @returns Array of available slots
* /
protected getAvailableContainersBotCanStoreItemsIn ( botInventory : PmcInventory ) : EquipmentSlots [ ]
{
const result = [ EquipmentSlots . POCKETS ] ;
2023-11-16 22:42:06 +01:00
if ( botInventory . items . find ( ( x ) = > x . slotId === EquipmentSlots . TACTICAL_VEST ) )
2023-10-10 13:03:20 +02:00
{
result . push ( EquipmentSlots . TACTICAL_VEST ) ;
}
2023-11-16 22:42:06 +01:00
if ( botInventory . items . find ( ( x ) = > x . slotId === EquipmentSlots . BACKPACK ) )
2023-10-10 13:03:20 +02:00
{
result . push ( EquipmentSlots . BACKPACK ) ;
}
return result ;
}
2023-07-27 13:15:06 +02:00
/ * *
* Force healing items onto bot to ensure they can heal in - raid
* @param botInventory Inventory to add items to
* @param botRole Role of bot ( sptBear / sptUsec )
* /
protected addForcedMedicalItemsToPmcSecure ( botInventory : PmcInventory , botRole : string ) : void
{
const grizzly = this . itemHelper . getItem ( "590c657e86f77412b013051d" ) [ 1 ] ;
2023-11-16 22:42:06 +01:00
this . addLootFromPool ( [ grizzly ] , [ EquipmentSlots . SECURED_CONTAINER ] , 2 , botInventory , botRole , false , 0 , true ) ;
2023-07-27 13:15:06 +02:00
const surv12 = this . itemHelper . getItem ( "5d02797c86f774203f38e30a" ) [ 1 ] ;
2023-11-16 22:42:06 +01:00
this . addLootFromPool ( [ surv12 ] , [ EquipmentSlots . SECURED_CONTAINER ] , 1 , botInventory , botRole , false , 0 , true ) ;
2023-07-27 13:15:06 +02:00
const morphine = this . itemHelper . getItem ( "544fb3f34bdc2d03748b456a" ) [ 1 ] ;
2023-11-16 22:42:06 +01:00
this . addLootFromPool ( [ morphine ] , [ EquipmentSlots . SECURED_CONTAINER ] , 3 , botInventory , botRole , false , 0 , true ) ;
2023-07-27 16:01:08 +02:00
const afak = this . itemHelper . getItem ( "60098ad7c2240c0fe85c570a" ) [ 1 ] ;
2023-11-16 22:42:06 +01:00
this . addLootFromPool ( [ afak ] , [ EquipmentSlots . SECURED_CONTAINER ] , 2 , botInventory , botRole , false , 0 , true ) ;
2023-07-27 13:15:06 +02:00
}
2023-10-10 13:03:20 +02:00
/ * *
* Get a biased random number
* @param min Smallest size
* @param max Biggest size
* @param nValue Value to bias choice
* @returns Chosen number
* /
2023-03-03 16:23:46 +01:00
protected getRandomisedCount ( min : number , max : number , nValue : number ) : number
{
const range = max - min ;
return this . randomUtil . getBiasedRandomNumber ( min , max , range , nValue ) ;
}
/ * *
2024-01-19 12:57:25 +01:00
* Take random items from a pool and add to an inventory until totalItemCount or totalValueLimit or space limit is reached
2023-10-10 13:03:20 +02:00
* @param pool Pool of items to pick from
* @param equipmentSlots What equipment slot will the loot items be added to
2023-03-03 16:23:46 +01:00
* @param totalItemCount Max count of items to add
2023-10-10 13:03:20 +02:00
* @param inventoryToAddItemsTo Bot inventory loot will be added to
* @param botRole Role of the bot loot is being generated for ( assault / pmcbot )
* @param useLimits Should item limit counts be used as defined in config / bot . json
* @param totalValueLimitRub Total value of loot allowed in roubles
* @param isPmc Is bot being generated for a pmc
2023-03-03 16:23:46 +01:00
* /
protected addLootFromPool (
pool : ITemplateItem [ ] ,
equipmentSlots : string [ ] ,
totalItemCount : number ,
inventoryToAddItemsTo : PmcInventory ,
botRole : string ,
useLimits = false ,
totalValueLimitRub = 0 ,
2023-11-16 22:42:06 +01:00
isPmc = false ,
) : void
2023-03-03 16:23:46 +01:00
{
// Loot pool has items
if ( pool . length )
{
let currentTotalRub = 0 ;
const itemLimits : Record < string , number > = { } ;
2024-01-19 12:57:25 +01:00
/** Prep limits for items added to the container */
2023-11-16 22:42:06 +01:00
const itemSpawnLimits : Record < string , Record < string , number > > = { } ;
2023-07-27 14:14:09 +02:00
let fitItemIntoContainerAttempts = 0 ;
2023-03-03 16:23:46 +01:00
for ( let i = 0 ; i < totalItemCount ; i ++ )
{
2024-01-19 10:52:56 +01:00
const itemToAddTemplate = this . getRandomItemFromPoolByBotRole ( pool , botRole ) ;
2024-01-19 12:57:25 +01:00
const newRootItemId = this . hashUtil . generate ( ) ;
const itemWithChildrenToAdd : Item [ ] = [ {
_id : newRootItemId ,
2023-03-03 16:23:46 +01:00
_tpl : itemToAddTemplate._id ,
2023-11-16 22:42:06 +01:00
. . . this . botGeneratorHelper . generateExtraPropertiesForItem ( itemToAddTemplate , botRole ) ,
2023-03-03 16:23:46 +01:00
} ] ;
if ( useLimits )
{
if ( Object . keys ( itemLimits ) . length === 0 )
{
this . initItemLimitArray ( isPmc , botRole , itemLimits ) ;
}
if ( ! itemSpawnLimits [ botRole ] )
{
itemSpawnLimits [ botRole ] = this . getItemSpawnLimitsForBotType ( isPmc , botRole ) ;
}
2023-11-16 22:42:06 +01:00
if (
this . itemHasReachedSpawnLimit (
itemToAddTemplate ,
botRole ,
isPmc ,
itemLimits ,
itemSpawnLimits [ botRole ] ,
)
)
2023-03-03 16:23:46 +01:00
{
i -- ;
continue ;
2023-11-16 22:42:06 +01:00
}
2023-03-03 16:23:46 +01:00
}
2024-01-19 12:57:25 +01:00
this . addRequiredChildItemsToParent ( itemToAddTemplate , itemWithChildrenToAdd , isPmc ) ;
2023-03-03 16:23:46 +01:00
2023-07-27 14:14:09 +02:00
// Attempt to add item to container(s)
2023-11-16 22:42:06 +01:00
const itemAddedResult = this . botWeaponGeneratorHelper . addItemWithChildrenToEquipmentSlot (
equipmentSlots ,
2024-01-19 12:57:25 +01:00
newRootItemId ,
2023-11-16 22:42:06 +01:00
itemToAddTemplate . _id ,
2024-01-19 12:57:25 +01:00
itemWithChildrenToAdd ,
2023-11-16 22:42:06 +01:00
inventoryToAddItemsTo ,
) ;
2023-12-08 17:27:34 +01:00
// Handle when item cannot be added
2023-11-24 17:05:58 +01:00
if ( itemAddedResult !== ItemAddedResult . SUCCESS )
2023-07-27 14:14:09 +02:00
{
2023-11-24 17:05:58 +01:00
if ( itemAddedResult === ItemAddedResult . NO_CONTAINERS )
{
// Bot has no container to put item in, exit
2023-12-08 17:27:34 +01:00
this . logger . debug ( ` Unable to add: ${ totalItemCount } items to bot as it lacks a container to include them ` ) ;
2023-11-24 17:05:58 +01:00
break ;
}
2023-07-27 14:14:09 +02:00
fitItemIntoContainerAttempts ++ ;
if ( fitItemIntoContainerAttempts >= 4 )
{
2023-11-16 22:42:06 +01:00
this . logger . debug (
2023-11-27 18:01:38 +01:00
` Failed to place item ${ i } of ${ totalItemCount } items into ${ botRole } containers: ${ equipmentSlots . join ( "," ) } . Tried ${ fitItemIntoContainerAttempts } times, reason: ${ ItemAddedResult [ itemAddedResult ] } , skipping ` ,
2023-11-16 22:42:06 +01:00
) ;
2023-07-27 14:14:09 +02:00
break ;
}
2023-12-08 17:27:34 +01:00
2024-01-19 12:57:25 +01:00
// Try again, failed but still under attempt limit
2023-12-08 17:27:34 +01:00
continue ;
2023-07-27 14:14:09 +02:00
}
2024-01-09 00:27:18 +01:00
// Item added okay, reset counter for next item
fitItemIntoContainerAttempts = 0 ;
2023-03-03 16:23:46 +01:00
// Stop adding items to bots pool if rolling total is over total limit
2023-10-12 14:25:13 +02:00
if ( totalValueLimitRub > 0 )
2023-03-03 16:23:46 +01:00
{
currentTotalRub += this . handbookHelper . getTemplatePrice ( itemToAddTemplate . _id ) ;
if ( currentTotalRub > totalValueLimitRub )
{
break ;
}
}
}
}
}
2024-01-19 12:57:25 +01:00
/ * *
* Some items need child items to function , add them to the itemToAddChildrenTo array
* @param itemToAddTemplate Db template of item to check
* @param itemToAddChildrenTo Item to add children to
* @param isPmc Is the item being generated for a pmc ( affects money / ammo stack sizes )
* /
protected addRequiredChildItemsToParent ( itemToAddTemplate : ITemplateItem , itemToAddChildrenTo : Item [ ] , isPmc : boolean ) : void
{
// Fill ammo box
if ( this . itemHelper . isOfBaseclass ( itemToAddTemplate . _id , BaseClasses . AMMO_BOX ) )
{
this . itemHelper . addCartridgesToAmmoBox ( itemToAddChildrenTo , itemToAddTemplate ) ;
}
// Make money a stack
else if ( this . itemHelper . isOfBaseclass ( itemToAddTemplate . _id , BaseClasses . MONEY ) )
{
this . randomiseMoneyStackSize ( isPmc , itemToAddTemplate , itemToAddChildrenTo [ 0 ] ) ;
}
// Make ammo a stack
else if ( this . itemHelper . isOfBaseclass ( itemToAddTemplate . _id , BaseClasses . AMMO ) )
{
this . randomiseAmmoStackSize ( isPmc , itemToAddTemplate , itemToAddChildrenTo [ 0 ] ) ;
}
// Must add soft inserts/plates
else if ( this . itemHelper . itemRequiresSoftInserts ( itemToAddTemplate . _id ) )
{
itemToAddChildrenTo = this . itemHelper . addChildSlotItems ( itemToAddChildrenTo , itemToAddTemplate , null , true ) ;
}
}
2023-03-03 16:23:46 +01:00
/ * *
* Add generated weapons to inventory as loot
* @param botInventory inventory to add preset to
* @param equipmentSlot slot to place the preset in ( backpack )
* @param templateInventory bots template , assault . json
* @param modChances chances for mods to spawn on weapon
2023-11-04 10:08:33 +01:00
* @param botRole bots role . e . g . pmcBot
2023-03-03 16:23:46 +01:00
* @param isPmc are we generating for a pmc
* /
2023-11-16 22:42:06 +01:00
protected addLooseWeaponsToInventorySlot (
sessionId : string ,
botInventory : PmcInventory ,
equipmentSlot : string ,
templateInventory : Inventory ,
modChances : ModsChances ,
botRole : string ,
isPmc : boolean ,
botLevel : number ,
) : void
2023-03-03 16:23:46 +01:00
{
2023-11-16 22:42:06 +01:00
const chosenWeaponType = this . randomUtil . getArrayValue ( [
EquipmentSlots . FIRST_PRIMARY_WEAPON ,
EquipmentSlots . FIRST_PRIMARY_WEAPON ,
EquipmentSlots . FIRST_PRIMARY_WEAPON ,
EquipmentSlots . HOLSTER ,
] ) ;
const randomisedWeaponCount = this . randomUtil . getInt (
this . pmcConfig . looseWeaponInBackpackLootMinMax . min ,
this . pmcConfig . looseWeaponInBackpackLootMinMax . max ,
) ;
2023-03-03 16:23:46 +01:00
if ( randomisedWeaponCount > 0 )
{
for ( let i = 0 ; i < randomisedWeaponCount ; i ++ )
{
2023-11-16 22:42:06 +01:00
const generatedWeapon = this . botWeaponGenerator . generateRandomWeapon (
sessionId ,
chosenWeaponType ,
templateInventory ,
botInventory . equipment ,
modChances ,
botRole ,
isPmc ,
botLevel ,
) ;
2023-11-24 16:26:41 +01:00
const result = this . botWeaponGeneratorHelper . addItemWithChildrenToEquipmentSlot (
2023-11-16 22:42:06 +01:00
[ equipmentSlot ] ,
generatedWeapon . weapon [ 0 ] . _id ,
generatedWeapon . weapon [ 0 ] . _tpl ,
[ . . . generatedWeapon . weapon ] ,
botInventory ,
) ;
2023-11-24 16:26:41 +01:00
if ( result !== ItemAddedResult . SUCCESS )
{
2023-11-24 17:05:58 +01:00
this . logger . debug ( ` Failed to add additional weapon ${ generatedWeapon . weapon [ 0 ] . _id } to bot backpack, reason: ${ ItemAddedResult [ result ] } ` ) ;
2023-11-24 16:26:41 +01:00
}
2023-03-03 16:23:46 +01:00
}
}
}
/ * *
* Get a random item from the pool parameter using the biasedRandomNumber system
2023-10-10 13:03:20 +02:00
* @param pool Pool of items to pick an item from
* @param isPmc Is the bot being created a pmc
2023-07-05 19:44:35 +02:00
* @returns ITemplateItem object
* /
2024-01-19 10:52:56 +01:00
protected getRandomItemFromPoolByBotRole ( pool : ITemplateItem [ ] , botRole : string ) : ITemplateItem
2023-07-05 19:44:35 +02:00
{
2023-11-16 22:42:06 +01:00
const itemIndex = this . randomUtil . getBiasedRandomNumber (
0 ,
pool . length - 1 ,
pool . length - 1 ,
this . getBotLootNValueByRole ( botRole ) ,
) ;
2023-07-05 19:44:35 +02:00
return pool [ itemIndex ] ;
}
/ * *
* Get the loot nvalue from botconfig
2023-10-10 13:03:20 +02:00
* @param botRole Role of bot e . g . assault / bosstagilla / sptBear
2023-07-05 19:44:35 +02:00
* @returns nvalue as number
* /
protected getBotLootNValueByRole ( botRole : string ) : number
{
const result = this . botConfig . lootNValue [ botRole ] ;
if ( ! result )
{
2023-07-19 14:16:45 +02:00
this . logger . warning ( this . localisationService . getText ( "bot-unable_to_find_loot_n_value_for_bot" , botRole ) ) ;
2023-07-05 19:44:35 +02:00
2023-11-16 22:42:06 +01:00
return this . botConfig . lootNValue . scav ;
2023-07-05 19:44:35 +02:00
}
return result ;
}
2023-03-03 16:23:46 +01:00
/ * *
2023-10-10 13:03:20 +02:00
* Hydrate item limit array to contain items that have a limit for a specific bot type
2023-03-03 16:23:46 +01:00
* All values are set to 0
2023-10-10 13:03:20 +02:00
* @param isPmc Is the bot a pmc
* @param botRole Role the bot has
2023-11-16 22:42:06 +01:00
* @param limitCount
2023-03-03 16:23:46 +01:00
* /
protected initItemLimitArray ( isPmc : boolean , botRole : string , limitCount : Record < string , number > ) : void
{
// Init current count of items we want to limit
const spawnLimits = this . getItemSpawnLimitsForBotType ( isPmc , botRole ) ;
for ( const limit in spawnLimits )
{
limitCount [ limit ] = 0 ;
}
}
2023-11-16 22:42:06 +01:00
2023-03-03 16:23:46 +01:00
/ * *
* Check if an item has reached its bot - specific spawn limit
* @param itemTemplate Item we check to see if its reached spawn limit
* @param botRole Bot type
* @param isPmc Is bot we ' re working with a pmc
2023-10-10 13:03:20 +02:00
* @param limitCount Spawn limits for items on bot
* @param itemSpawnLimits The limits this bot is allowed to have
2023-03-03 16:23:46 +01:00
* @returns true if item has reached spawn limit
* /
2023-11-16 22:42:06 +01:00
protected itemHasReachedSpawnLimit (
itemTemplate : ITemplateItem ,
botRole : string ,
isPmc : boolean ,
limitCount : Record < string , number > ,
itemSpawnLimits : Record < string , number > ,
) : boolean
2023-03-03 16:23:46 +01:00
{
// PMCs and scavs have different sections of bot config for spawn limits
if ( ! ! itemSpawnLimits && itemSpawnLimits . length === 0 )
{
// No items found in spawn limit, drop out
return false ;
}
// No spawn limits, skipping
if ( ! itemSpawnLimits )
{
return false ;
}
const idToCheckFor = this . getMatchingIdFromSpawnLimits ( itemTemplate , itemSpawnLimits ) ;
if ( ! idToCheckFor )
{
// ParentId or tplid not found in spawnLimits, not a spawn limited item, skip
return false ;
}
// Increment item count with this bot type
limitCount [ idToCheckFor ] ++ ;
// return true, we are over limit
if ( limitCount [ idToCheckFor ] > itemSpawnLimits [ idToCheckFor ] )
{
2023-06-27 18:23:21 +02:00
// Prevent edge-case of small loot pools + code trying to add limited item over and over infinitely
2023-03-03 16:23:46 +01:00
if ( limitCount [ idToCheckFor ] > itemSpawnLimits [ idToCheckFor ] * 10 )
{
2023-11-16 22:42:06 +01:00
this . logger . debug (
this . localisationService . getText ( "bot-item_spawn_limit_reached_skipping_item" , {
botRole : botRole ,
itemName : itemTemplate._name ,
attempts : limitCount [ idToCheckFor ] ,
} ) ,
) ;
2023-03-03 16:23:46 +01:00
return false ;
}
return true ;
}
return false ;
}
/ * *
* Randomise the stack size of a money object , uses different values for pmc or scavs
2023-11-04 10:08:33 +01:00
* @param isPmc Is money on a PMC bot
* @param itemTemplate item details from db
* @param moneyItem Money item to randomise
2023-03-03 16:23:46 +01:00
* /
protected randomiseMoneyStackSize ( isPmc : boolean , itemTemplate : ITemplateItem , moneyItem : Item ) : void
{
2023-11-04 10:08:33 +01:00
// PMCs have a different stack max size
const minStackSize = itemTemplate . _props . StackMinRandom ;
2023-11-16 22:42:06 +01:00
const maxStackSize = isPmc
2023-11-04 10:08:33 +01:00
? this . pmcConfig . dynamicLoot . moneyStackLimits [ itemTemplate . _id ]
: itemTemplate . _props . StackMaxRandom ;
const randomSize = this . randomUtil . getInt ( minStackSize , maxStackSize ) ;
if ( ! moneyItem . upd )
2023-03-03 16:23:46 +01:00
{
2023-11-04 10:08:33 +01:00
moneyItem . upd = { } ;
2023-03-03 16:23:46 +01:00
}
2023-11-04 10:08:33 +01:00
2023-11-16 22:42:06 +01:00
moneyItem . upd . StackObjectsCount = randomSize ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Randomise the size of an ammo stack
2023-11-04 10:08:33 +01:00
* @param isPmc Is ammo on a PMC bot
* @param itemTemplate item details from db
* @param ammoItem Ammo item to randomise
2023-03-03 16:23:46 +01:00
* /
protected randomiseAmmoStackSize ( isPmc : boolean , itemTemplate : ITemplateItem , ammoItem : Item ) : void
{
2023-11-04 10:08:33 +01:00
const randomSize = itemTemplate . _props . StackMaxSize === 1
? 1
2023-12-17 23:36:47 +01:00
: this . randomUtil . getInt ( itemTemplate . _props . StackMinRandom , Math . min ( itemTemplate . _props . StackMaxRandom , 60 ) ) ;
2023-03-03 16:23:46 +01:00
2023-11-04 10:08:33 +01:00
if ( ! ammoItem . upd )
{
ammoItem . upd = { } ;
2023-03-03 16:23:46 +01:00
}
2023-11-04 10:08:33 +01:00
2023-11-16 22:42:06 +01:00
ammoItem . upd . StackObjectsCount = randomSize ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Get spawn limits for a specific bot type from bot . json config
* If no limit found for a non pmc bot , fall back to defaults
* @param isPmc is the bot we want limits for a pmc
* @param botRole what role does the bot have
* @returns Dictionary of tplIds and limit
* /
protected getItemSpawnLimitsForBotType ( isPmc : boolean , botRole : string ) : Record < string , number >
{
if ( isPmc )
{
2023-11-16 22:42:06 +01:00
return this . botConfig . itemSpawnLimits . pmc ;
2023-03-03 16:23:46 +01:00
}
if ( this . botConfig . itemSpawnLimits [ botRole . toLowerCase ( ) ] )
{
return this . botConfig . itemSpawnLimits [ botRole . toLowerCase ( ) ] ;
}
2023-11-16 22:42:06 +01:00
this . logger . warning (
this . localisationService . getText ( "bot-unable_to_find_spawn_limits_fallback_to_defaults" , botRole ) ,
) ;
2023-03-03 16:23:46 +01:00
2023-11-16 22:42:06 +01:00
return this . botConfig . itemSpawnLimits . default ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Get the parentId or tplId of item inside spawnLimits object if it exists
* @param itemTemplate item we want to look for in spawn limits
* @param spawnLimits Limits to check for item
* @returns id as string , otherwise undefined
* /
protected getMatchingIdFromSpawnLimits ( itemTemplate : ITemplateItem , spawnLimits : Record < string , number > ) : string
{
if ( itemTemplate . _id in spawnLimits )
{
return itemTemplate . _id ;
}
// tplId not found in spawnLimits, check if parentId is
if ( itemTemplate . _parent in spawnLimits )
{
return itemTemplate . _parent ;
}
// parentId and tplid not found
return undefined ;
}
2023-11-16 22:42:06 +01:00
}