diff --git a/project/assets/configs/inraid.json b/project/assets/configs/inraid.json index 06f0474f..92dcebc4 100644 --- a/project/assets/configs/inraid.json +++ b/project/assets/configs/inraid.json @@ -27,5 +27,6 @@ "scavExtractGain": 0.01, "pmcKillProbabilityForScavGain": 0.2, "keepFiRSecureContainerOnDeath": false, + "alwaysKeepFoundInRaidonRaidEnd": true, "playerScavHostileChancePercent": 15 } diff --git a/project/src/helpers/InRaidHelper.ts b/project/src/helpers/InRaidHelper.ts index ff958ea7..4cf0d0d7 100644 --- a/project/src/helpers/InRaidHelper.ts +++ b/project/src/helpers/InRaidHelper.ts @@ -3,15 +3,18 @@ import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { Item } from "@spt/models/eft/common/tables/IItem"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { IInRaidConfig } from "@spt/models/spt/config/IInRaidConfig"; import { ILostOnDeathConfig } from "@spt/models/spt/config/ILostOnDeathConfig"; import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ConfigServer } from "@spt/servers/ConfigServer"; +import { DatabaseService } from "@spt/services/DatabaseService"; import { ICloner } from "@spt/utils/cloners/ICloner"; import { inject, injectable } from "tsyringe"; @injectable() export class InRaidHelper { protected lostOnDeathConfig: ILostOnDeathConfig; + protected inRaidConfig: IInRaidConfig; constructor( @inject("PrimaryLogger") protected logger: ILogger, @@ -19,8 +22,10 @@ export class InRaidHelper { @inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ConfigServer") protected configServer: ConfigServer, @inject("PrimaryCloner") protected cloner: ICloner, + @inject("DatabaseService") protected databaseService: DatabaseService, ) { this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH); + this.inRaidConfig = this.configServer.getConfig(ConfigTypes.IN_RAID); } /** @@ -43,7 +48,12 @@ export class InRaidHelper { * @param serverProfile Profile to update * @param postRaidProfile Profile returned by client after a raid */ - public setInventory(sessionID: string, serverProfile: IPmcData, postRaidProfile: IPmcData): void { + public setInventory( + sessionID: string, + serverProfile: IPmcData, + postRaidProfile: IPmcData, + isSurvived: boolean, + ): void { // Store insurance (as removeItem() removes insurance also) const insured = this.cloner.clone(serverProfile.InsuredItems); @@ -52,12 +62,46 @@ export class InRaidHelper { this.inventoryHelper.removeItem(serverProfile, serverProfile.Inventory.questRaidItems, sessionID); this.inventoryHelper.removeItem(serverProfile, serverProfile.Inventory.sortingTable, sessionID); + // Handle Removing of FIR status if did not survive. + if (!isSurvived && !this.inRaidConfig.alwaysKeepFoundInRaidonRaidEnd) { + this.removeSpawnedInSessionPropertyFromItems(postRaidProfile); + } + // Add the new items serverProfile.Inventory.items = [...postRaidProfile.Inventory.items, ...serverProfile.Inventory.items]; serverProfile.Inventory.fastPanel = postRaidProfile.Inventory.fastPanel; // Quick access items bar serverProfile.InsuredItems = insured; } + /** + * 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 + */ + public removeSpawnedInSessionPropertyFromItems(postRaidProfile: IPmcData): IPmcData { + const dbItems = this.databaseService.getItems(); + const itemsToRemovePropertyFrom = postRaidProfile.Inventory.items.filter((item) => { + // Has upd object + upd.SpawnedInSession property + not a quest item + return ( + "upd" in item && + "SpawnedInSession" in item.upd && + !dbItems[item._tpl]._props.QuestItem && + !( + this.inRaidConfig.keepFiRSecureContainerOnDeath && + this.itemHelper.itemIsInsideContainer(item, "SecuredContainer", postRaidProfile.Inventory.items) + ) + ); + }); + + for (const item of itemsToRemovePropertyFrom) { + // biome-ignore lint/performance/noDelete: + delete item.upd.SpawnedInSession; + } + + return postRaidProfile; + } + /** * Clear PMC inventory of all items except those that are exempt * Used post-raid to remove items after death diff --git a/project/src/models/spt/config/IInRaidConfig.ts b/project/src/models/spt/config/IInRaidConfig.ts index ce36f0c9..28ea94f7 100644 --- a/project/src/models/spt/config/IInRaidConfig.ts +++ b/project/src/models/spt/config/IInRaidConfig.ts @@ -20,6 +20,8 @@ export interface IInRaidConfig extends IBaseConfig { pmcKillProbabilityForScavGain: number; /** On death should items in your secure keep their Find in raid status regardless of how you finished the raid */ keepFiRSecureContainerOnDeath: boolean; + /** If enabled always keep found in raid status on items */ + alwaysKeepFoundInRaidonRaidEnd: boolean; /** Percentage chance a player scav hot is hostile to the player when scavving */ playerScavHostileChancePercent: number; } diff --git a/project/src/services/LocationLifecycleService.ts b/project/src/services/LocationLifecycleService.ts index a4a34285..aecb9bc3 100644 --- a/project/src/services/LocationLifecycleService.ts +++ b/project/src/services/LocationLifecycleService.ts @@ -188,6 +188,7 @@ export class LocationLifecycleService { const isPmc = serverDetails[1].toLowerCase() === "pmc"; const mapBase = this.databaseService.getLocation(locationName).base; const isDead = this.isPlayerDead(request.results); + const isSurvived = this.isPlayerSurvived(request.results); if (!isPmc) { this.handlePostRaidPlayerScav(sessionId, pmcProfile, scavProfile, isDead, request); @@ -195,7 +196,16 @@ export class LocationLifecycleService { return; } - this.handlePostRaidPmc(sessionId, pmcProfile, scavProfile, postRaidProfile, isDead, request, locationName); + this.handlePostRaidPmc( + sessionId, + pmcProfile, + scavProfile, + postRaidProfile, + isDead, + isSurvived, + request, + locationName, + ); // Handle car extracts if (this.extractWasViaCar(request.results.exitName)) { @@ -420,11 +430,12 @@ export class LocationLifecycleService { scavProfile: IPmcData, postRaidProfile: IPmcData, isDead: boolean, + isSurvived: boolean, request: IEndLocalRaidRequestData, locationName: string, ): void { // Update inventory - this.inRaidHelper.setInventory(sessionId, pmcProfile, postRaidProfile); + this.inRaidHelper.setInventory(sessionId, pmcProfile, postRaidProfile, isSurvived); pmcProfile.Info.Level = postRaidProfile.Info.Level; pmcProfile.Skills = postRaidProfile.Skills; @@ -637,6 +648,15 @@ export class LocationLifecycleService { return inventoryItems; } + /** + * Checks to see if player survives. run through will return false + * @param statusOnExit Exit value from offraidData object + * @returns true if Survived + */ + protected isPlayerSurvived(results: IEndRaidResult): boolean { + return results.result.toLowerCase() === "survived"; + } + /** * Is the player dead after a raid - dead = anything other than "survived" / "runner" * @param statusOnExit Exit value from offraidData object