diff --git a/project/src/models/eft/common/tables/IBotBase.ts b/project/src/models/eft/common/tables/IBotBase.ts index 0492562b..13cb4026 100644 --- a/project/src/models/eft/common/tables/IBotBase.ts +++ b/project/src/models/eft/common/tables/IBotBase.ts @@ -341,6 +341,7 @@ export interface BackendCounter export interface InsuredItem { + /** Trader Id item was insured by */ tid: string itemId: string } diff --git a/project/src/models/eft/inRaid/IInsuredItemsData.ts b/project/src/models/eft/inRaid/IInsuredItemsData.ts new file mode 100644 index 00000000..55367fd0 --- /dev/null +++ b/project/src/models/eft/inRaid/IInsuredItemsData.ts @@ -0,0 +1,7 @@ +export interface IInsuredItemsData +{ + id: string + durability?: number + maxDurability?: number + hits?: number +} \ No newline at end of file diff --git a/project/src/models/eft/inRaid/ISaveProgressRequestData.ts b/project/src/models/eft/inRaid/ISaveProgressRequestData.ts index 48a40e02..3d4d5a18 100644 --- a/project/src/models/eft/inRaid/ISaveProgressRequestData.ts +++ b/project/src/models/eft/inRaid/ISaveProgressRequestData.ts @@ -1,6 +1,7 @@ import { PlayerRaidEndState } from "../../../models/enums/PlayerRaidEndState"; import { IPmcData } from "../common/IPmcData"; import { ISyncHealthRequestData } from "../health/ISyncHealthRequestData"; +import { IInsuredItemsData } from "./IInsuredItemsData"; export interface ISaveProgressRequestData { @@ -8,4 +9,5 @@ export interface ISaveProgressRequestData profile: IPmcData isPlayerScav: boolean health: ISyncHealthRequestData + insurance: IInsuredItemsData[] } \ No newline at end of file diff --git a/project/src/services/InsuranceService.ts b/project/src/services/InsuranceService.ts index 3ad6b57a..43bf14d7 100644 --- a/project/src/services/InsuranceService.ts +++ b/project/src/services/InsuranceService.ts @@ -8,8 +8,8 @@ import { NotificationSendHelper } from "../helpers/NotificationSendHelper"; import { SecureContainerHelper } from "../helpers/SecureContainerHelper"; import { TraderHelper } from "../helpers/TraderHelper"; import { IPmcData } from "../models/eft/common/IPmcData"; -import { InsuredItem } from "../models/eft/common/tables/IBotBase"; import { Item } from "../models/eft/common/tables/IItem"; +import { IInsuredItemsData } from "../models/eft/inRaid/IInsuredItemsData"; import { ISaveProgressRequestData } from "../models/eft/inRaid/ISaveProgressRequestData"; import { IUserDialogInfo } from "../models/eft/profile/IAkiProfile"; import { ConfigTypes } from "../models/enums/ConfigTypes"; @@ -21,6 +21,7 @@ import { ILogger } from "../models/spt/utils/ILogger"; import { ConfigServer } from "../servers/ConfigServer"; import { DatabaseServer } from "../servers/DatabaseServer"; import { SaveServer } from "../servers/SaveServer"; +import { JsonUtil } from "../utils/JsonUtil"; import { RandomUtil } from "../utils/RandomUtil"; import { TimeUtil } from "../utils/TimeUtil"; import { LocaleService } from "./LocaleService"; @@ -38,6 +39,7 @@ export class InsuranceService @inject("SecureContainerHelper") protected secureContainerHelper: SecureContainerHelper, @inject("RandomUtil") protected randomUtil: RandomUtil, @inject("ItemHelper") protected itemHelper: ItemHelper, + @inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("TimeUtil") protected timeUtil: TimeUtil, @inject("SaveServer") protected saveServer: SaveServer, @inject("TraderHelper") protected traderHelper: TraderHelper, @@ -52,6 +54,11 @@ export class InsuranceService this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE); } + /** + * Does player have insurance array + * @param sessionId Player id + * @returns True if exists + */ public insuranceExists(sessionId: string): boolean { return this.insured[sessionId] !== undefined; @@ -203,7 +210,7 @@ export class InsuranceService } /** - * Store lost gear post-raid inside profile + * Store lost gear post-raid inside profile, ready for later code to pick it up and mail it * @param pmcData player profile to store gear in * @param offraidData post-raid request object * @param preRaidGear gear player wore prior to raid @@ -218,27 +225,136 @@ export class InsuranceService const equipmentToSendToPlayer = []; for (const insuredItem of pmcData.InsuredItems) { - // Check insured item was on player during raid - if (preRaidGearHash[insuredItem.itemId]) + // Skip insured items not on player when they started raid + const preRaidItem = preRaidGearHash[insuredItem.itemId]; + if (!preRaidItem) { - // This item exists in preRaidGear, meaning we brought it into the raid - // Check if item missing OR player died with item on - if (!offRaidGearHash[insuredItem.itemId] || playerDied) - { - equipmentToSendToPlayer.push({ - "pmcData": pmcData, - "insuredItem": insuredItem, - "item": preRaidGearHash[insuredItem.itemId], - "sessionID": sessionID - }); - } + continue; + } + + // Skip items we should never return + if (this.insuranceConfig.blacklistedEquipment.includes(preRaidItem.slotId)) + { + continue; + } + + // Check if item missing in post-raid gear OR player died + // Catches both events: player died with item on + player survied but dropped item in raid + if (!offRaidGearHash[insuredItem.itemId] || playerDied) + { + equipmentToSendToPlayer.push({ + pmcData: pmcData, + itemToReturnToPlayer: this.getInsuredItemDetails(pmcData, preRaidItem, offraidData.insurance.find(x => x.id === insuredItem.itemId)), + traderId: insuredItem.tid, + sessionID: sessionID + }); } } // Process all insured items lost in-raid for (const gear of equipmentToSendToPlayer) { - this.addGearToSend(gear.pmcData, gear.insuredItem, gear.item, gear.sessionID); + this.addGearToSend(gear); + } + } + + /** + * Take preraid item and update properties to ensure its ready to be given to player in insurance return mail + * @param pmcData Player profile + * @param insuredItem Insured items properties + * @param preRaidItem Insured item as it was pre-raid + * @param insuredItemFromClient Item data when player left raid (durability values) + * @returns Item object + */ + protected getInsuredItemDetails(pmcData: IPmcData, preRaidItem: Item, insuredItemFromClient: IInsuredItemsData): Item + { + // Get baseline item to return, clone pre raid item + const itemToReturn: Item = this.jsonUtil.clone(preRaidItem); + + // Add upd if it doesnt exist + if (!itemToReturn.upd) + { + itemToReturn.upd = {}; + } + + // Check for slotid values that need to be updated and adjust + this.updateSlotIdValue(pmcData.Inventory.equipment, itemToReturn); + + // Remove location property + if (itemToReturn.slotId === "hideout" && "location" in itemToReturn) + { + delete itemToReturn.location; + } + + // Remove found in raid status when upd exists + SpawnedInSession value exists + if ("upd" in itemToReturn && "SpawnedInSession" in itemToReturn.upd) + { + itemToReturn.upd.SpawnedInSession = false; + } + + // Client item has durability values, Ensure values persist into server data + if (insuredItemFromClient.durability) + { + // Item didnt have Repairable object pre-raid, add it + if (!itemToReturn.upd.Repairable) + { + itemToReturn.upd.Repairable = { + Durability: insuredItemFromClient.durability, + MaxDurability: insuredItemFromClient.maxDurability + }; + } + else + { + itemToReturn.upd.Repairable.Durability = insuredItemFromClient.durability; + itemToReturn.upd.Repairable.MaxDurability = insuredItemFromClient.maxDurability; + } + + } + + // Client item has FaceShield values, Ensure values persist into server data + if (insuredItemFromClient?.hits) + { + // Item didnt have faceshield object pre-raid, add it + if (!itemToReturn.upd.FaceShield) + { + itemToReturn.upd.FaceShield = { + Hits: insuredItemFromClient.hits + }; + } + else + { + itemToReturn.upd.FaceShield.Hits = insuredItemFromClient.hits; + } + + } + + return itemToReturn; + } + + /** + * Reset slotId property to "hideout" when necessary (used to be in ) + * @param pmcData Players pmcData.Inventory.equipment value + * @param itemToReturn item we will send to player as insurance return + */ + protected updateSlotIdValue(playerBaseInventoryEquipmentId: string, itemToReturn: Item): void + { + const pocketSlots = [ + "pocket1", + "pocket2", + "pocket3", + "pocket4" + ]; + + // Some pockets can lose items with player death, some don't + if (!("slotId" in itemToReturn) || pocketSlots.includes(itemToReturn.slotId)) + { + itemToReturn.slotId = "hideout"; + } + + // Mark root-level items for later processing + if (itemToReturn.parentId === playerBaseInventoryEquipmentId) + { + itemToReturn.slotId = "hideout"; } } @@ -260,67 +376,36 @@ export class InsuranceService /** * Add gear item to InsuredItems array in player profile - * @param pmcData profile to store item in - * @param insuredItem Item to store in profile - * @param actualItem item to store * @param sessionID Session id + * @param pmcData Player profile + * @param itemToReturnToPlayer item to store + * @param traderId Id of trader item was insured with */ - protected addGearToSend(pmcData: IPmcData, insuredItem: InsuredItem, actualItem: Item, sessionID: string): void + protected addGearToSend(gear: { sessionID: string; pmcData: IPmcData; itemToReturnToPlayer: Item; traderId: string}): void { - // Skip items defined in config - if (this.insuranceConfig.blacklistedEquipment.includes(actualItem.slotId)) + const sessionId = gear.sessionID; + const pmcData = gear.pmcData; + const itemToReturnToPlayer = gear.itemToReturnToPlayer; + const traderId = gear.traderId; + + // Ensure insurance array is init + if (!this.insuranceExists(sessionId)) { - return; + this.resetInsurance(sessionId); } - const pocketSlots = [ - "pocket1", - "pocket2", - "pocket3", - "pocket4" - ]; - - // Check and correct the validity of the slotId - if (!("slotId" in actualItem) || pocketSlots.includes(actualItem.slotId)) + // init trader insurance array + if (!this.insuranceTraderArrayExists(sessionId, traderId)) { - actualItem.slotId = "hideout"; + this.resetInsuranceTraderArray(sessionId, traderId); } - // Mark root-level items for later - if (actualItem.parentId === pmcData.Inventory.equipment) - { - actualItem.slotId = "hideout"; - } + this.addInsuranceItemToArray(sessionId, traderId, itemToReturnToPlayer); - // Clear the location attribute of the item in the container. - if (actualItem.slotId === "hideout" && "location" in actualItem) - { - delete actualItem.location; - } - - // Remove found in raid status - if ("upd" in actualItem && "SpawnedInSession" in actualItem.upd) - { - actualItem.upd.SpawnedInSession = false; - } - - // Mark to add to insurance - if (!this.insuranceExists(sessionID)) - { - this.resetInsurance(sessionID); - } - - if (!this.insuranceTraderArrayExists(sessionID, insuredItem.tid)) - { - this.resetInsuranceTraderArray(sessionID, insuredItem.tid); - } - - this.addInsuranceItemToArray(sessionID, insuredItem.tid, actualItem); - - // Remove processed item from array + // Remove item from insured items array as its been processed pmcData.InsuredItems = pmcData.InsuredItems.filter((item) => { - return item.itemId !== insuredItem.itemId; + return item.itemId !== itemToReturnToPlayer._id; }); } @@ -353,6 +438,7 @@ export class InsuranceService */ public addInsuranceItemToArray(sessionId: string, traderId: string, itemToAdd: Item): void { + this.insured[sessionId][traderId].push(itemToAdd); }