2023-03-03 16:23:46 +01:00
import { inject , injectable } from "tsyringe" ;
2023-10-10 13:03:20 +02:00
import { IPmcData , IPostRaidPmcData } from "../models/eft/common/IPmcData" ;
import { IQuestStatus , TraderInfo , Victim } from "../models/eft/common/tables/IBotBase" ;
2023-03-03 16:23:46 +01:00
import { Item } from "../models/eft/common/tables/IItem" ;
import { ISaveProgressRequestData } from "../models/eft/inRaid/ISaveProgressRequestData" ;
2023-07-25 20:50:46 +02:00
import { IFailQuestRequestData } from "../models/eft/quests/IFailQuestRequestData" ;
2023-03-03 16:23:46 +01:00
import { ConfigTypes } from "../models/enums/ConfigTypes" ;
2023-07-25 20:50:46 +02:00
import { QuestStatus } from "../models/enums/QuestStatus" ;
2023-10-10 13:03:20 +02:00
import { Traders } from "../models/enums/Traders" ;
import { IInRaidConfig } from "../models/spt/config/IInRaidConfig" ;
2023-03-03 16:23:46 +01:00
import { ILostOnDeathConfig } from "../models/spt/config/ILostOnDeathConfig" ;
import { ILogger } from "../models/spt/utils/ILogger" ;
import { ConfigServer } from "../servers/ConfigServer" ;
import { DatabaseServer } from "../servers/DatabaseServer" ;
import { SaveServer } from "../servers/SaveServer" ;
import { LocalisationService } from "../services/LocalisationService" ;
import { ProfileFixerService } from "../services/ProfileFixerService" ;
import { JsonUtil } from "../utils/JsonUtil" ;
import { InventoryHelper } from "./InventoryHelper" ;
import { ItemHelper } from "./ItemHelper" ;
import { PaymentHelper } from "./PaymentHelper" ;
2023-07-25 20:50:46 +02:00
import { QuestHelper } from "./QuestHelper" ;
2023-03-03 16:23:46 +01:00
@injectable ( )
export class InRaidHelper
{
protected lostOnDeathConfig : ILostOnDeathConfig ;
2023-10-10 13:03:20 +02:00
protected inRaidConfig : IInRaidConfig ;
2023-03-03 16:23:46 +01:00
constructor (
@inject ( "WinstonLogger" ) protected logger : ILogger ,
@inject ( "SaveServer" ) protected saveServer : SaveServer ,
@inject ( "JsonUtil" ) protected jsonUtil : JsonUtil ,
@inject ( "ItemHelper" ) protected itemHelper : ItemHelper ,
@inject ( "DatabaseServer" ) protected databaseServer : DatabaseServer ,
@inject ( "InventoryHelper" ) protected inventoryHelper : InventoryHelper ,
2023-07-25 20:50:46 +02:00
@inject ( "QuestHelper" ) protected questHelper : QuestHelper ,
2023-03-03 16:23:46 +01:00
@inject ( "PaymentHelper" ) protected paymentHelper : PaymentHelper ,
@inject ( "LocalisationService" ) protected localisationService : LocalisationService ,
@inject ( "ProfileFixerService" ) protected profileFixerService : ProfileFixerService ,
@inject ( "ConfigServer" ) protected configServer : ConfigServer
)
{
this . lostOnDeathConfig = this . configServer . getConfig ( ConfigTypes . LOST_ON_DEATH ) ;
2023-10-10 13:03:20 +02:00
this . inRaidConfig = this . configServer . getConfig ( ConfigTypes . IN_RAID ) ;
2023-03-03 16:23:46 +01:00
}
/ * *
2023-07-24 16:52:55 +02:00
* Lookup quest item loss from lostOnDeath config
2023-03-03 16:23:46 +01:00
* @returns True if items should be removed from inventory
* /
public removeQuestItemsOnDeath ( ) : boolean
{
return this . lostOnDeathConfig . questItems ;
}
/ * *
2023-08-10 15:29:09 +02:00
* Check items array and add an upd object to money with a stack count of 1
2023-03-03 16:23:46 +01:00
* Single stack money items have no upd object and thus no StackObjectsCount , causing issues
* @param items Items array to check
* /
public addUpdToMoneyFromRaid ( items : Item [ ] ) : void
{
for ( const item of items )
{
if ( this . paymentHelper . isMoneyTpl ( item . _tpl ) )
{
if ( ! item . upd )
{
item . upd = { } ;
}
if ( ! item . upd . StackObjectsCount )
{
item . upd . StackObjectsCount = 1 ;
}
}
}
}
/ * *
* Add karma changes up and return the new value
* @param existingFenceStanding Current fence standing level
* @param victims Array of kills player performed
* @returns adjusted karma level after kills are taken into account
* /
public calculateFenceStandingChangeFromKills ( existingFenceStanding : number , victims : Victim [ ] ) : number
{
2023-04-22 17:41:00 +02:00
// Run callback on every victim, adding up the standings gained/lossed, starting value is existing fence standing
const newFenceStanding = victims . reduce ( ( acc , victim ) = >
2023-03-03 16:23:46 +01:00
{
2023-10-10 13:03:20 +02:00
const standingForKill = this . getFenceStandingChangeForKillAsScav ( victim ) ;
2023-03-03 16:23:46 +01:00
if ( standingForKill )
{
2023-04-22 17:41:00 +02:00
return acc + standingForKill ;
2023-03-03 16:23:46 +01:00
}
2023-04-22 17:41:00 +02:00
this . logger . warning ( this . localisationService . getText ( "inraid-missing_standing_for_kill" , { victimSide : victim.Side , victimRole : victim.Role } ) ) ;
return acc ;
} , existingFenceStanding ) ;
2023-03-03 16:23:46 +01:00
2023-04-22 17:41:00 +02:00
return newFenceStanding ;
}
/ * *
* Get the standing gain / loss for killing an npc
* @param victim Who was killed by player
* @returns a numerical standing gain or loss
* /
2023-10-10 13:03:20 +02:00
protected getFenceStandingChangeForKillAsScav ( victim : Victim ) : number
2023-04-22 17:41:00 +02:00
{
const botTypes = this . databaseServer . getTables ( ) . bots . types ;
if ( victim . Side . toLowerCase ( ) === "savage" )
{
// Scavs and bosses
return botTypes [ victim . Role . toLowerCase ( ) ] ? . experience ? . standingForKill ;
}
2023-10-10 13:03:20 +02:00
// PMCs - get by bear/usec
2023-04-22 17:41:00 +02:00
return botTypes [ victim . Side . toLowerCase ( ) ] ? . experience ? . standingForKill ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Reset a profile to a baseline , used post - raid
* Reset points earned during session property
* Increment exp
* Remove Labs keycard
* @param profileData Profile to update
* @param saveProgressRequest post raid save data request data
* @param sessionID Session id
* @returns Reset profile object
* /
public updateProfileBaseStats ( profileData : IPmcData , saveProgressRequest : ISaveProgressRequestData , sessionID : string ) : IPmcData
{
// remove old skill fatigue
2023-07-15 12:03:54 +02:00
this . resetSkillPointsEarnedDuringRaid ( saveProgressRequest . profile ) ;
2023-03-03 16:23:46 +01:00
// set profile data
profileData . Info . Level = saveProgressRequest . profile . Info . Level ;
profileData . Skills = saveProgressRequest . profile . Skills ;
2023-10-10 13:03:20 +02:00
profileData . Stats . Eft = saveProgressRequest . profile . Stats . Eft ;
2023-03-03 16:23:46 +01:00
profileData . Encyclopedia = saveProgressRequest . profile . Encyclopedia ;
profileData . ConditionCounters = saveProgressRequest . profile . ConditionCounters ;
2023-07-25 20:50:46 +02:00
2023-08-07 12:13:17 +02:00
for ( const backendCounterKey in saveProgressRequest . profile . BackendCounters )
{
2023-10-17 23:04:19 +02:00
// Skip counters with no id
2023-08-07 20:42:23 +02:00
if ( ! saveProgressRequest . profile . BackendCounters [ backendCounterKey ] . id )
{
continue ;
}
2023-08-07 12:13:17 +02:00
const matchingPreRaidCounter = profileData . BackendCounters [ backendCounterKey ] ;
if ( ! matchingPreRaidCounter )
{
this . logger . error ( ` Backendcounter: ${ backendCounterKey } cannot be found in pre-raid data ` ) ;
continue ;
}
2023-08-07 20:42:23 +02:00
2023-08-07 12:13:17 +02:00
if ( matchingPreRaidCounter . value !== saveProgressRequest . profile . BackendCounters [ backendCounterKey ] . value )
{
2023-10-17 23:04:19 +02:00
this . logger . error ( ` Backendcounter: ${ backendCounterKey } value is different post raid, old: ${ matchingPreRaidCounter . value } new: ${ saveProgressRequest . profile . BackendCounters [ backendCounterKey ] . value } ` ) ;
2023-08-07 12:13:17 +02:00
}
}
2023-07-25 20:50:46 +02:00
this . processFailedQuests ( sessionID , profileData , profileData . Quests , saveProgressRequest . profile . Quests ) ;
2023-03-03 16:23:46 +01:00
profileData . Quests = saveProgressRequest . profile . Quests ;
// Transfer effects from request to profile
this . transferPostRaidLimbEffectsToProfile ( saveProgressRequest , profileData ) ;
2023-07-30 12:30:31 +02:00
this . applyTraderStandingAdjustments ( profileData . TradersInfo , saveProgressRequest . profile . TradersInfo ) ;
2023-03-03 16:23:46 +01:00
profileData . SurvivorClass = saveProgressRequest . profile . SurvivorClass ;
2023-10-17 23:04:19 +02:00
// Add experience points
2023-10-10 13:03:20 +02:00
profileData . Info . Experience += profileData . Stats . Eft . TotalSessionExperience ;
profileData . Stats . Eft . TotalSessionExperience = 0 ;
2023-03-03 16:23:46 +01:00
// Remove the Lab card
this . removeMapAccessKey ( saveProgressRequest , sessionID ) ;
this . setPlayerInRaidLocationStatusToNone ( sessionID ) ;
if ( ! saveProgressRequest . isPlayerScav )
{
this . profileFixerService . checkForAndFixPmcProfileIssues ( profileData ) ;
}
return profileData ;
}
2023-07-30 12:30:31 +02:00
2023-07-25 20:50:46 +02:00
/ * *
2023-07-25 22:14:18 +02:00
* Look for quests not are now status = fail that were not failed pre - raid and run the failQuest ( ) function
2023-07-25 20:50:46 +02:00
* @param sessionId Player id
* @param pmcData Player profile
* @param preRaidQuests Quests prior to starting raid
* @param postRaidQuests Quest after raid
* /
2023-10-10 13:03:20 +02:00
protected processFailedQuests ( sessionId : string , pmcData : IPmcData , preRaidQuests : IQuestStatus [ ] , postRaidQuests : IQuestStatus [ ] ) : void
2023-07-25 20:50:46 +02:00
{
2023-08-03 09:38:36 +02:00
if ( ! preRaidQuests )
{
// No quests to compare against, skip
return ;
}
2023-07-25 20:50:46 +02:00
// Loop over all quests from post-raid profile
for ( const postRaidQuest of postRaidQuests )
{
// Find matching pre-raid quest
2023-08-03 09:38:36 +02:00
const preRaidQuest = preRaidQuests ? . find ( x = > x . qid === postRaidQuest . qid ) ;
2023-07-25 20:50:46 +02:00
if ( preRaidQuest )
{
2023-07-25 22:14:18 +02:00
// Post-raid quest is failed but wasn't pre-raid
// postRaidQuest.status has a weird value, need to do some nasty casting to compare it
2023-07-25 21:37:45 +02:00
if ( < string > < unknown > postRaidQuest . status === "Fail" && preRaidQuest . status !== QuestStatus . Fail )
2023-07-25 20:50:46 +02:00
{
// Send failed message
const failBody : IFailQuestRequestData = {
Action : "QuestComplete" ,
qid : postRaidQuest.qid ,
removeExcessItems : true
} ;
this . questHelper . failQuest ( pmcData , failBody , sessionId ) ;
}
}
}
}
2023-07-15 12:03:54 +02:00
protected resetSkillPointsEarnedDuringRaid ( profile : IPmcData ) : void
{
for ( const skill of profile . Skills . Common )
{
skill . PointsEarnedDuringSession = 0.0 ;
}
}
2023-03-03 16:23:46 +01:00
/ * *
* Take body part effects from client profile and apply to server profile
* @param saveProgressRequest post - raid request
* @param profileData player profile on server
* /
protected transferPostRaidLimbEffectsToProfile ( saveProgressRequest : ISaveProgressRequestData , profileData : IPmcData ) : void
{
// Iterate over each body part
for ( const bodyPartId in saveProgressRequest . profile . Health . BodyParts )
{
// Get effects on body part from profile
const bodyPartEffects = saveProgressRequest . profile . Health . BodyParts [ bodyPartId ] . Effects ;
for ( const effect in bodyPartEffects )
{
const effectDetails = bodyPartEffects [ effect ] ;
// Null guard
if ( ! profileData . Health . BodyParts [ bodyPartId ] . Effects )
{
profileData . Health . BodyParts [ bodyPartId ] . Effects = { } ;
}
// Already exists on server profile, skip
const profileBodyPartEffects = profileData . Health . BodyParts [ bodyPartId ] . Effects ;
if ( profileBodyPartEffects [ effect ] )
{
continue ;
}
// Add effect to server profile
profileBodyPartEffects [ effect ] = { Time : effectDetails.Time ? ? - 1 } ;
}
}
}
2023-10-10 13:03:20 +02:00
/ * *
* Adjust server trader settings if they differ from data sent by client
* @param tradersServerProfile Server
* @param tradersClientProfile Client
* /
protected applyTraderStandingAdjustments ( tradersServerProfile : Record < string , TraderInfo > , tradersClientProfile : Record < string , TraderInfo > ) : void
2023-07-30 12:30:31 +02:00
{
2023-10-10 13:03:20 +02:00
for ( const traderId in tradersClientProfile )
2023-07-30 12:30:31 +02:00
{
2023-10-10 13:03:20 +02:00
if ( traderId === Traders . FENCE )
2023-07-30 12:30:31 +02:00
{
2023-10-10 13:03:20 +02:00
// Taking a car extract adjusts fence rep values via client/match/offline/end, skip fence for this check
2023-07-30 12:30:31 +02:00
continue ;
}
2023-10-10 13:03:20 +02:00
const serverProfileTrader = tradersServerProfile [ traderId ] ;
const clientProfileTrader = tradersClientProfile [ traderId ] ;
if ( ! ( serverProfileTrader && clientProfileTrader ) )
2023-07-30 12:30:31 +02:00
{
2023-10-10 13:03:20 +02:00
continue ;
2023-07-30 12:30:31 +02:00
}
2023-10-10 13:03:20 +02:00
if ( clientProfileTrader . standing !== serverProfileTrader . standing )
{
// Difference found, update server profile with values from client profile
tradersServerProfile [ traderId ] . standing = clientProfileTrader . standing ;
}
2023-07-30 12:30:31 +02:00
}
}
2023-03-03 16:23:46 +01:00
/ * *
* Some maps have one - time - use keys ( e . g . Labs
* Remove the relevant key from an inventory based on the post - raid request data passed in
* @param offraidData post - raid data
* @param sessionID Session id
* /
protected removeMapAccessKey ( offraidData : ISaveProgressRequestData , sessionID : string ) : void
{
const locationName = this . saveServer . getProfile ( sessionID ) . inraid . location . toLowerCase ( ) ;
const mapKey = this . databaseServer . getTables ( ) . locations [ locationName ] . base . AccessKeys [ 0 ] ;
if ( ! mapKey )
{
return ;
}
for ( const item of offraidData . profile . Inventory . items )
{
if ( item . _tpl === mapKey && item . slotId . toLowerCase ( ) !== "hideout" )
{
this . inventoryHelper . removeItem ( offraidData . profile , item . _id , sessionID ) ;
break ;
}
}
}
/ * *
* Set the SPT inraid location Profile property to 'none'
* @param sessionID Session id
* /
protected setPlayerInRaidLocationStatusToNone ( sessionID : string ) : void
{
this . saveServer . getProfile ( sessionID ) . inraid . location = "none" ;
}
/ * *
* Iterate over inventory items and remove the property that defines an item as Found in Raid
* Only removes property if item had FiR when entering raid
* @param postRaidProfile profile to update items for
* @returns Updated profile with SpawnedInSession removed
* /
2023-10-10 13:03:20 +02:00
public removeSpawnedInSessionPropertyFromItems ( postRaidProfile : IPostRaidPmcData ) : IPostRaidPmcData
2023-03-03 16:23:46 +01:00
{
2023-04-22 17:30:57 +02:00
const dbItems = this . databaseServer . getTables ( ) . templates . items ;
const itemsToRemovePropertyFrom = postRaidProfile . Inventory . items . filter ( x = >
2023-03-03 16:23:46 +01:00
{
2023-04-22 17:30:57 +02:00
// Has upd object + upd.SpawnedInSession property + not a quest item
2023-10-10 13:03:20 +02:00
return "upd" in x && "SpawnedInSession" in x . upd
&& ! dbItems [ x . _tpl ] . _props . QuestItem
&& ! ( this . inRaidConfig . keepFiRSecureContainerOnDeath && this . itemHelper . itemIsInsideContainer ( x , "SecuredContainer" , postRaidProfile . Inventory . items ) ) ;
2023-04-22 17:30:57 +02:00
} ) ;
itemsToRemovePropertyFrom . forEach ( item = >
{
delete item . upd . SpawnedInSession ;
} ) ;
2023-03-03 16:23:46 +01:00
return postRaidProfile ;
}
/ * *
* Update a players inventory post - raid
* Remove equipped items from pre - raid
* Add new items found in raid to profile
* Store insurance items in profile
* @param sessionID Session id
2023-10-11 11:47:54 +02:00
* @param serverProfile Profile to update
2023-03-03 16:23:46 +01:00
* @param postRaidProfile Profile returned by client after a raid
* @returns Updated profile
* /
2023-10-11 11:47:54 +02:00
public setInventory ( sessionID : string , serverProfile : IPmcData , postRaidProfile : IPmcData ) : IPmcData
2023-03-03 16:23:46 +01:00
{
2023-10-11 11:47:54 +02:00
// Store insurance (as removeItem() removes insurance also)
const insured = this . jsonUtil . clone ( serverProfile . InsuredItems ) ;
2023-03-03 16:23:46 +01:00
2023-10-11 11:47:54 +02:00
// Remove possible equipped items from before the raid
this . inventoryHelper . removeItem ( serverProfile , serverProfile . Inventory . equipment , sessionID ) ;
this . inventoryHelper . removeItem ( serverProfile , serverProfile . Inventory . questRaidItems , sessionID ) ;
this . inventoryHelper . removeItem ( serverProfile , serverProfile . Inventory . sortingTable , sessionID ) ;
2023-03-03 16:23:46 +01:00
2023-10-11 11:47:54 +02:00
// Add the new items
serverProfile . Inventory . items = [ . . . postRaidProfile . Inventory . items , . . . serverProfile . Inventory . items ] ;
serverProfile . Inventory . fastPanel = postRaidProfile . Inventory . fastPanel ;
serverProfile . InsuredItems = insured ;
2023-03-03 16:23:46 +01:00
2023-10-11 11:47:54 +02:00
return serverProfile ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Clear pmc inventory of all items except those that are exempt
* Used post - raid to remove items after death
* @param pmcData Player profile
* @param sessionID Session id
* /
public deleteInventory ( pmcData : IPmcData , sessionID : string ) : void
{
2023-04-22 17:27:46 +02:00
// Get inventory item ids to remove from players profile
const itemIdsToDeleteFromProfile = this . getInventoryItemsLostOnDeath ( pmcData ) . map ( x = > x . _id ) ;
itemIdsToDeleteFromProfile . forEach ( x = >
2023-03-03 16:23:46 +01:00
{
2023-04-22 17:27:46 +02:00
this . inventoryHelper . removeItem ( pmcData , x , sessionID ) ;
} ) ;
// Remove contents of fast panel
pmcData . Inventory . fastPanel = { } ;
}
/ * *
* Get an array of items from a profile that will be lost on death
* @param pmcProfile Profile to get items from
* @returns Array of items lost on death
* /
protected getInventoryItemsLostOnDeath ( pmcProfile : IPmcData ) : Item [ ]
{
const inventoryItems = pmcProfile . Inventory . items ? ? [ ] ;
const equipment = pmcProfile ? . Inventory ? . equipment ;
const questRaidItems = pmcProfile ? . Inventory ? . questRaidItems ;
return inventoryItems . filter ( x = >
{
// Keep items flagged as kept after death
if ( this . isItemKeptAfterDeath ( pmcProfile , x ) )
2023-03-03 16:23:46 +01:00
{
2023-04-22 17:27:46 +02:00
return false ;
2023-03-03 16:23:46 +01:00
}
2023-04-22 17:27:46 +02:00
2023-03-03 16:23:46 +01:00
// Remove normal items or quest raid items
2023-04-22 17:27:46 +02:00
if ( x . parentId === equipment || x . parentId === questRaidItems )
2023-03-03 16:23:46 +01:00
{
2023-04-22 17:27:46 +02:00
return true ;
2023-03-03 16:23:46 +01:00
}
2023-04-22 17:27:46 +02:00
// Pocket items are not lost on death
if ( x . slotId . startsWith ( "pocket" ) )
2023-03-03 16:23:46 +01:00
{
2023-04-22 17:27:46 +02:00
return true ;
2023-03-03 16:23:46 +01:00
}
2023-04-22 17:27:46 +02:00
return false ;
} ) ;
2023-03-03 16:23:46 +01:00
}
/ * *
* Get items in vest / pocket / backpack inventory containers ( excluding children )
* @param pmcData Player profile
* @returns Item array
* /
protected getBaseItemsInRigPocketAndBackpack ( pmcData : IPmcData ) : Item [ ]
{
const rig = pmcData . Inventory . items . find ( x = > x . slotId === "TacticalVest" ) ;
const pockets = pmcData . Inventory . items . find ( x = > x . slotId === "Pockets" ) ;
const backpack = pmcData . Inventory . items . find ( x = > x . slotId === "Backpack" ) ;
const baseItemsInRig = pmcData . Inventory . items . filter ( x = > x . parentId === rig ? . _id ) ;
const baseItemsInPockets = pmcData . Inventory . items . filter ( x = > x . parentId === pockets ? . _id ) ;
const baseItemsInBackpack = pmcData . Inventory . items . filter ( x = > x . parentId === backpack ? . _id ) ;
return [ . . . baseItemsInRig , . . . baseItemsInPockets , . . . baseItemsInBackpack ] ;
}
/ * *
* Does the provided items slotId mean its kept on the player after death
* @pmcData Player profile
* @itemToCheck Item to check should be kept
* @returns true if item is kept after death
* /
2023-03-03 18:53:28 +01:00
protected isItemKeptAfterDeath ( pmcData : IPmcData , itemToCheck : Item ) : boolean
2023-03-03 16:23:46 +01:00
{
2023-03-03 18:53:28 +01:00
// No parentid means its a base inventory item, always keep
if ( ! itemToCheck . parentId )
{
return true ;
}
2023-03-03 16:23:46 +01:00
// Is item equipped on player
if ( itemToCheck . parentId === pmcData . Inventory . equipment )
{
2023-03-03 18:53:28 +01:00
// Check slot id against config, true = delete, false = keep, undefined = delete
const discard = this . lostOnDeathConfig . equipment [ itemToCheck . slotId ] ;
if ( discard === undefined )
2023-03-03 16:23:46 +01:00
{
return false ;
}
2023-03-03 18:53:28 +01:00
return ! discard ;
2023-03-03 16:23:46 +01:00
}
// Is quest item + quest item not lost on death
if ( ! this . lostOnDeathConfig . questItems && itemToCheck . parentId === pmcData . Inventory . questRaidItems )
{
return true ;
}
2023-03-03 18:53:28 +01:00
// special slots are always kept after death
if ( itemToCheck . slotId ? . includes ( "SpecialSlot" ) && this . lostOnDeathConfig . specialSlotItems )
2023-03-03 16:23:46 +01:00
{
return true ;
}
return false ;
}
/ * *
* Return the equipped items from a players inventory
* @param items Players inventory to search through
* @returns an array of equipped items
* /
public getPlayerGear ( items : Item [ ] ) : Item [ ]
{
// Player Slots we care about
const inventorySlots = [
"FirstPrimaryWeapon" ,
"SecondPrimaryWeapon" ,
"Holster" ,
"Scabbard" ,
"Compass" ,
"Headwear" ,
"Earpiece" ,
"Eyewear" ,
"FaceCover" ,
"ArmBand" ,
"ArmorVest" ,
"TacticalVest" ,
"Backpack" ,
"pocket1" ,
"pocket2" ,
"pocket3" ,
"pocket4" ,
"SecuredContainer"
] ;
let inventoryItems : Item [ ] = [ ] ;
// Get an array of root player items
for ( const item of items )
{
if ( inventorySlots . includes ( item . slotId ) )
{
inventoryItems . push ( item ) ;
}
}
// Loop through these items and get all of their children
let newItems = inventoryItems ;
while ( newItems . length > 0 )
{
const foundItems = [ ] ;
for ( const item of newItems )
{
// Find children of this item
for ( const newItem of items )
{
if ( newItem . parentId === item . _id )
{
foundItems . push ( newItem ) ;
}
}
}
// Add these new found items to our list of inventory items
inventoryItems = [
. . . inventoryItems ,
. . . foundItems
] ;
// Now find the children of these items
newItems = foundItems ;
}
return inventoryItems ;
}
}