2023-03-03 15:23:46 +00:00
|
|
|
import { inject, injectable } from "tsyringe";
|
2023-10-19 17:21:17 +00:00
|
|
|
import { DialogueHelper } from "@spt-aki/helpers/DialogueHelper";
|
|
|
|
import { HandbookHelper } from "@spt-aki/helpers/HandbookHelper";
|
|
|
|
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
|
|
|
import { SecureContainerHelper } from "@spt-aki/helpers/SecureContainerHelper";
|
|
|
|
import { TraderHelper } from "@spt-aki/helpers/TraderHelper";
|
|
|
|
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
|
|
|
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
|
|
|
import { ITraderBase } from "@spt-aki/models/eft/common/tables/ITrader";
|
|
|
|
import { IInsuredItemsData } from "@spt-aki/models/eft/inRaid/IInsuredItemsData";
|
|
|
|
import { ISaveProgressRequestData } from "@spt-aki/models/eft/inRaid/ISaveProgressRequestData";
|
2024-01-29 10:42:02 +00:00
|
|
|
import { BonusType } from "@spt-aki/models/enums/BonusType";
|
2023-10-19 17:21:17 +00:00
|
|
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
|
|
|
import { MessageType } from "@spt-aki/models/enums/MessageType";
|
|
|
|
import { IInsuranceConfig } from "@spt-aki/models/spt/config/IInsuranceConfig";
|
2024-02-06 10:10:56 +00:00
|
|
|
import { ILostOnDeathConfig } from "@spt-aki/models/spt/config/ILostOnDeathConfig";
|
2024-02-08 15:56:45 -05:00
|
|
|
import { IInsuranceEquipmentPkg } from "@spt-aki/models/spt/services/IInsuranceEquipmentPkg";
|
2023-10-19 17:21:17 +00:00
|
|
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
|
|
|
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
|
|
|
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
|
|
|
import { SaveServer } from "@spt-aki/servers/SaveServer";
|
|
|
|
import { LocaleService } from "@spt-aki/services/LocaleService";
|
|
|
|
import { LocalisationService } from "@spt-aki/services/LocalisationService";
|
|
|
|
import { MailSendService } from "@spt-aki/services/MailSendService";
|
2024-05-13 17:58:17 +00:00
|
|
|
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
|
2024-02-08 15:56:45 -05:00
|
|
|
import { HashUtil } from "@spt-aki/utils/HashUtil";
|
2023-10-19 17:21:17 +00:00
|
|
|
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
|
|
|
|
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
@injectable()
|
|
|
|
export class InsuranceService
|
|
|
|
{
|
2023-05-27 22:39:32 +01:00
|
|
|
protected insured: Record<string, Record<string, Item[]>> = {};
|
2023-03-03 15:23:46 +00:00
|
|
|
protected insuranceConfig: IInsuranceConfig;
|
2024-02-06 10:10:56 +00:00
|
|
|
protected lostOnDeathConfig: ILostOnDeathConfig;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
constructor(
|
|
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
|
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
|
|
@inject("SecureContainerHelper") protected secureContainerHelper: SecureContainerHelper,
|
|
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
2023-07-20 16:04:26 +01:00
|
|
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
2024-02-08 15:56:45 -05:00
|
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
2023-03-03 15:23:46 +00:00
|
|
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
|
|
|
@inject("SaveServer") protected saveServer: SaveServer,
|
|
|
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
|
|
|
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
|
|
|
|
@inject("HandbookHelper") protected handbookHelper: HandbookHelper,
|
|
|
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
2023-06-01 21:24:23 +01:00
|
|
|
@inject("LocaleService") protected localeService: LocaleService,
|
2023-08-07 22:43:00 +01:00
|
|
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
2023-11-13 11:13:25 -05:00
|
|
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
2024-05-13 17:58:17 +00:00
|
|
|
@inject("RecursiveCloner") protected cloner: ICloner,
|
2023-03-03 15:23:46 +00:00
|
|
|
)
|
|
|
|
{
|
|
|
|
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
|
2024-02-06 10:10:56 +00:00
|
|
|
this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-08-05 17:26:16 +00:00
|
|
|
/**
|
|
|
|
* Does player have insurance array
|
|
|
|
* @param sessionId Player id
|
|
|
|
* @returns True if exists
|
|
|
|
*/
|
2023-03-03 15:23:46 +00:00
|
|
|
public insuranceExists(sessionId: string): boolean
|
|
|
|
{
|
|
|
|
return this.insured[sessionId] !== undefined;
|
|
|
|
}
|
|
|
|
|
2023-07-20 16:04:26 +01:00
|
|
|
/**
|
|
|
|
* Get all insured items by all traders for a profile
|
|
|
|
* @param sessionId Profile id (session id)
|
|
|
|
* @returns Item array
|
|
|
|
*/
|
2023-05-27 22:39:32 +01:00
|
|
|
public getInsurance(sessionId: string): Record<string, Item[]>
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
return this.insured[sessionId];
|
|
|
|
}
|
|
|
|
|
2023-07-20 16:04:26 +01:00
|
|
|
/**
|
|
|
|
* Get insured items by profile id + trader id
|
|
|
|
* @param sessionId Profile id (session id)
|
|
|
|
* @param traderId Trader items were insured with
|
|
|
|
* @returns Item array
|
|
|
|
*/
|
2023-03-03 15:23:46 +00:00
|
|
|
public getInsuranceItems(sessionId: string, traderId: string): Item[]
|
|
|
|
{
|
2023-05-27 22:39:32 +01:00
|
|
|
return this.insured[sessionId][traderId];
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public resetInsurance(sessionId: string): void
|
|
|
|
{
|
|
|
|
this.insured[sessionId] = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends stored insured items as message to player
|
2023-07-20 16:04:26 +01:00
|
|
|
* @param pmcData profile to send insured items to
|
2023-03-03 15:23:46 +00:00
|
|
|
* @param sessionID SessionId of current player
|
|
|
|
* @param mapId Id of the map player died/exited that caused the insurance to be issued on
|
|
|
|
*/
|
|
|
|
public sendInsuredItems(pmcData: IPmcData, sessionID: string, mapId: string): void
|
|
|
|
{
|
2024-02-02 11:57:10 +00:00
|
|
|
// Get insurance items for each trader
|
2023-03-03 15:23:46 +00:00
|
|
|
for (const traderId in this.getInsurance(sessionID))
|
|
|
|
{
|
2023-07-20 16:04:26 +01:00
|
|
|
const traderBase = this.traderHelper.getTrader(traderId, sessionID);
|
|
|
|
const insuranceReturnTimestamp = this.getInsuranceReturnTimestamp(pmcData, traderBase);
|
2023-03-03 15:23:46 +00:00
|
|
|
const dialogueTemplates = this.databaseServer.getTables().traders[traderId].dialogue;
|
|
|
|
|
2024-02-02 11:57:10 +00:00
|
|
|
const systemData = {
|
2023-06-01 21:24:23 +01:00
|
|
|
date: this.timeUtil.getDateMailFormat(),
|
|
|
|
time: this.timeUtil.getTimeMailFormat(),
|
2024-02-02 13:54:07 -05:00
|
|
|
location: mapId,
|
2023-03-03 15:23:46 +00:00
|
|
|
};
|
2024-02-02 11:57:10 +00:00
|
|
|
// Send "i will go look for your stuff" message from trader to player
|
|
|
|
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
|
|
|
sessionID,
|
|
|
|
this.traderHelper.getTraderById(traderId),
|
|
|
|
MessageType.NPC_TRADER,
|
|
|
|
this.randomUtil.getArrayValue(dialogueTemplates.insuranceStart),
|
|
|
|
null,
|
2024-05-17 15:32:41 -04:00
|
|
|
this.timeUtil.getHoursAsSeconds(
|
|
|
|
this.databaseServer.getTables().globals.config.Insurance.MaxStorageTimeInHour,
|
|
|
|
),
|
2024-02-02 13:54:07 -05:00
|
|
|
systemData,
|
2024-02-02 11:57:10 +00:00
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2024-02-02 11:57:10 +00:00
|
|
|
// Store insurance to send to player later in profile
|
|
|
|
// Store insurance return details in profile + "hey i found your stuff, here you go!" message details to send to player at a later date
|
2023-03-03 15:23:46 +00:00
|
|
|
this.saveServer.getProfile(sessionID).insurance.push({
|
|
|
|
scheduledTime: insuranceReturnTimestamp,
|
|
|
|
traderId: traderId,
|
2024-02-02 11:57:10 +00:00
|
|
|
maxStorageTime: this.timeUtil.getHoursAsSeconds(traderBase.insurance.max_storage_time),
|
|
|
|
systemData: systemData,
|
|
|
|
messageType: MessageType.INSURANCE_RETURN,
|
|
|
|
messageTemplateId: this.randomUtil.getArrayValue(dialogueTemplates.insuranceFound),
|
2023-11-13 11:13:25 -05:00
|
|
|
items: this.getInsurance(sessionID)[traderId],
|
2023-03-03 15:23:46 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
this.resetInsurance(sessionID);
|
|
|
|
}
|
|
|
|
|
2023-07-20 16:04:26 +01:00
|
|
|
/**
|
|
|
|
* Check all root insured items and remove location property + set slotId to 'hideout'
|
|
|
|
* @param sessionId Session id
|
|
|
|
* @param traderId Trader id
|
|
|
|
*/
|
2023-04-24 14:03:41 +01:00
|
|
|
protected removeLocationProperty(sessionId: string, traderId: string): void
|
|
|
|
{
|
2023-05-27 22:39:32 +01:00
|
|
|
const insuredItems = this.getInsurance(sessionId)[traderId];
|
|
|
|
for (const insuredItem of this.getInsurance(sessionId)[traderId])
|
2023-04-24 14:03:41 +01:00
|
|
|
{
|
2023-07-20 16:04:26 +01:00
|
|
|
// Find insured items parent
|
2024-05-17 15:32:41 -04:00
|
|
|
const insuredItemsParent = insuredItems.find((x) => x._id === insuredItem.parentId);
|
2023-07-20 16:04:26 +01:00
|
|
|
if (!insuredItemsParent)
|
2023-04-24 14:03:41 +01:00
|
|
|
{
|
2023-07-20 16:04:26 +01:00
|
|
|
// Remove location + set slotId of insured items parent
|
2023-04-24 14:03:41 +01:00
|
|
|
insuredItem.slotId = "hideout";
|
|
|
|
delete insuredItem.location;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
/**
|
2023-07-20 16:04:26 +01:00
|
|
|
* Get a timestamp of when insurance items should be sent to player based on trader used to insure
|
|
|
|
* Apply insurance return bonus if found in profile
|
2023-03-03 15:23:46 +00:00
|
|
|
* @param pmcData Player profile
|
2023-07-20 16:04:26 +01:00
|
|
|
* @param trader Trader base used to insure items
|
2023-03-03 15:23:46 +00:00
|
|
|
* @returns Timestamp to return items to player in seconds
|
|
|
|
*/
|
|
|
|
protected getInsuranceReturnTimestamp(pmcData: IPmcData, trader: ITraderBase): number
|
|
|
|
{
|
|
|
|
// If override inconfig is non-zero, use that instead of trader values
|
|
|
|
if (this.insuranceConfig.returnTimeOverrideSeconds > 0)
|
|
|
|
{
|
2023-11-13 11:13:25 -05:00
|
|
|
this.logger.debug(
|
|
|
|
`Insurance override used: returning in ${this.insuranceConfig.returnTimeOverrideSeconds} seconds`,
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
return this.timeUtil.getTimestamp() + this.insuranceConfig.returnTimeOverrideSeconds;
|
|
|
|
}
|
|
|
|
|
2024-05-17 15:32:41 -04:00
|
|
|
const insuranceReturnTimeBonus = pmcData.Bonuses.find((b) => b.type === BonusType.INSURANCE_RETURN_TIME);
|
|
|
|
const insuranceReturnTimeBonusPercent
|
|
|
|
= 1.0 - (insuranceReturnTimeBonus ? Math.abs(insuranceReturnTimeBonus.value) : 0) / 100;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-11-16 10:09:01 -05:00
|
|
|
const traderMinReturnAsSeconds = trader.insurance.min_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS;
|
|
|
|
const traderMaxReturnAsSeconds = trader.insurance.max_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS;
|
2023-03-03 15:23:46 +00:00
|
|
|
const randomisedReturnTimeSeconds = this.randomUtil.getInt(traderMinReturnAsSeconds, traderMaxReturnAsSeconds);
|
|
|
|
|
2023-07-20 16:04:26 +01:00
|
|
|
// Current time + randomised time calculated above
|
2024-05-07 23:57:08 -04:00
|
|
|
return this.timeUtil.getTimestamp() + randomisedReturnTimeSeconds * insuranceReturnTimeBonusPercent;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-02-08 15:56:45 -05:00
|
|
|
* Create insurance equipment packages that should be sent to the user. The packages should contain items that have
|
|
|
|
* been lost in a raid and should be returned to the player through the insurance system.
|
|
|
|
*
|
|
|
|
* NOTE: We do not have data on items that were dropped in a raid. This means we have to pull item data from the
|
|
|
|
* profile at the start of the raid to return to the player in insurance. Because of this, the item
|
|
|
|
* positioning may differ from the position the item was in when the player died. Apart from removing all
|
|
|
|
* positioning, this is the best we can do. >:{}
|
|
|
|
*
|
2024-02-04 10:19:57 +00:00
|
|
|
* @param pmcData Player profile
|
|
|
|
* @param offraidData Post-raid data
|
|
|
|
* @param preRaidGear Pre-raid data
|
2023-03-03 15:23:46 +00:00
|
|
|
* @param sessionID Session id
|
2024-02-04 10:19:57 +00:00
|
|
|
* @param playerDied Did player die in raid
|
|
|
|
* @returns Array of insured items lost in raid
|
2023-03-03 15:23:46 +00:00
|
|
|
*/
|
2024-02-04 10:19:57 +00:00
|
|
|
public getGearLostInRaid(
|
2023-11-13 11:13:25 -05:00
|
|
|
pmcData: IPmcData,
|
|
|
|
offraidData: ISaveProgressRequestData,
|
|
|
|
preRaidGear: Item[],
|
|
|
|
sessionID: string,
|
|
|
|
playerDied: boolean,
|
2024-02-08 15:56:45 -05:00
|
|
|
): IInsuranceEquipmentPkg[]
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2024-02-08 15:56:45 -05:00
|
|
|
const equipmentPkg: IInsuranceEquipmentPkg[] = [];
|
|
|
|
const preRaidGearMap = this.itemHelper.generateItemsMap(preRaidGear);
|
|
|
|
const offRaidGearMap = this.itemHelper.generateItemsMap(offraidData.profile.Inventory.items);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
for (const insuredItem of pmcData.InsuredItems)
|
|
|
|
{
|
2024-02-08 15:56:45 -05:00
|
|
|
// Skip insured items not on player when they started the raid.
|
|
|
|
if (!preRaidGearMap.has(insuredItem.itemId))
|
2023-08-05 17:26:16 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-02-08 15:56:45 -05:00
|
|
|
const preRaidItem = preRaidGearMap.get(insuredItem.itemId);
|
|
|
|
|
2024-01-08 21:16:53 +00:00
|
|
|
// Skip slots we should never return as they're never lost on death
|
2023-08-05 17:26:16 +00:00
|
|
|
if (this.insuranceConfig.blacklistedEquipment.includes(preRaidItem.slotId))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-02-08 15:56:45 -05:00
|
|
|
// Equipment slots can be flagged as never lost on death and shouldn't be saved in an insurance package.
|
|
|
|
// We need to check if the item is directly equipped to an equipment slot, or if it is a child Item of an
|
|
|
|
// equipment slot.
|
|
|
|
const equipmentParentItem = this.itemHelper.getEquipmentParent(preRaidItem._id, preRaidGearMap);
|
|
|
|
|
|
|
|
// Now that we have the equipment parent item, we can check to see if that item is located in an equipment
|
|
|
|
// slot that is flagged as lost on death. If it is, then the itemShouldBeLostOnDeath.
|
|
|
|
const itemShouldBeLostOnDeath = this.lostOnDeathConfig.equipment[equipmentParentItem?.slotId] ?? true;
|
2024-02-06 10:10:56 +00:00
|
|
|
|
2024-02-08 15:56:45 -05:00
|
|
|
// Was the item found in the player inventory post-raid?
|
|
|
|
const itemOnPlayerPostRaid = offRaidGearMap.has(insuredItem.itemId);
|
2024-02-06 10:10:56 +00:00
|
|
|
|
|
|
|
// Check if item missing in post-raid gear OR player died + item slot flagged as lost on death
|
2024-01-11 12:48:10 +00:00
|
|
|
// Catches both events: player died with item on + player survived but dropped item in raid
|
2024-05-13 17:03:47 -04:00
|
|
|
if (!itemOnPlayerPostRaid || (playerDied && itemShouldBeLostOnDeath))
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2024-02-08 15:56:45 -05:00
|
|
|
equipmentPkg.push({
|
2023-08-05 17:26:16 +00:00
|
|
|
pmcData: pmcData,
|
2024-02-04 22:41:11 +00:00
|
|
|
itemToReturnToPlayer: this.getInsuredItemDetails(
|
2024-02-02 13:54:07 -05:00
|
|
|
pmcData,
|
2024-02-04 22:41:11 +00:00
|
|
|
preRaidItem,
|
2024-05-17 15:32:41 -04:00
|
|
|
offraidData.insurance?.find((insuranceItem) => insuranceItem.id === insuredItem.itemId),
|
2024-02-02 13:54:07 -05:00
|
|
|
),
|
2023-08-05 17:26:16 +00:00
|
|
|
traderId: insuredItem.tid,
|
2023-11-13 11:13:25 -05:00
|
|
|
sessionID: sessionID,
|
2023-08-05 17:26:16 +00:00
|
|
|
});
|
2024-02-04 22:41:11 +00:00
|
|
|
|
|
|
|
// Armor item with slots, we need to include soft_inserts as they can never be removed from armor items
|
|
|
|
if (this.itemHelper.armorItemCanHoldMods(preRaidItem._tpl))
|
|
|
|
{
|
|
|
|
if (this.itemHelper.itemHasSlots(preRaidItem._tpl))
|
|
|
|
{
|
|
|
|
// Get IDs of all soft insert child items on armor from pre raid gear data
|
2024-05-17 15:32:41 -04:00
|
|
|
const softInsertChildIds = preRaidGear
|
|
|
|
.filter(
|
|
|
|
(item) =>
|
|
|
|
item.parentId === preRaidItem._id
|
|
|
|
&& this.itemHelper.getSoftInsertSlotIds().includes(item.slotId.toLowerCase()),
|
|
|
|
)
|
|
|
|
.map((x) => x._id);
|
2024-02-04 22:41:11 +00:00
|
|
|
|
|
|
|
// Add all items found above to return data
|
|
|
|
for (const softInsertChildModId of softInsertChildIds)
|
|
|
|
{
|
2024-02-08 15:56:45 -05:00
|
|
|
equipmentPkg.push({
|
2024-02-04 22:41:11 +00:00
|
|
|
pmcData: pmcData,
|
|
|
|
itemToReturnToPlayer: this.getInsuredItemDetails(
|
|
|
|
pmcData,
|
2024-05-17 15:32:41 -04:00
|
|
|
preRaidGear.find((item) => item._id === softInsertChildModId),
|
|
|
|
offraidData.insurance?.find(
|
|
|
|
(insuranceItem) => insuranceItem.id === softInsertChildModId,
|
2024-02-04 22:41:11 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
traderId: insuredItem.tid,
|
|
|
|
sessionID: sessionID,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-08 15:56:45 -05:00
|
|
|
return equipmentPkg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Take the insurance item packages within a profile session and ensure that each of the items in that package are
|
|
|
|
* not orphaned from their parent ID.
|
|
|
|
*
|
|
|
|
* @param sessionID The session ID to update insurance equipment packages in.
|
|
|
|
* @returns void
|
|
|
|
*/
|
|
|
|
protected adoptOrphanedInsEquipment(sessionID: string): void
|
|
|
|
{
|
|
|
|
const rootID = this.getRootItemParentID(sessionID);
|
|
|
|
const insuranceData = this.getInsurance(sessionID);
|
|
|
|
for (const [traderId, items] of Object.entries(insuranceData))
|
|
|
|
{
|
|
|
|
this.insured[sessionID][traderId] = this.itemHelper.adoptOrphanedItems(rootID, items);
|
|
|
|
}
|
2024-02-04 10:19:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store lost gear post-raid inside profile, ready for later code to pick it up and mail it
|
2024-02-08 15:56:45 -05:00
|
|
|
* @param equipmentPkg Gear to store - generated by getGearLostInRaid()
|
2024-02-04 10:19:57 +00:00
|
|
|
*/
|
2024-02-08 15:56:45 -05:00
|
|
|
public storeGearLostInRaidToSendLater(sessionID: string, equipmentPkg: IInsuranceEquipmentPkg[]): void
|
2024-02-04 10:19:57 +00:00
|
|
|
{
|
2023-03-03 17:53:28 +00:00
|
|
|
// Process all insured items lost in-raid
|
2024-02-08 15:56:45 -05:00
|
|
|
for (const gear of equipmentPkg)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-08-05 17:26:16 +00:00
|
|
|
this.addGearToSend(gear);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
2024-02-08 15:56:45 -05:00
|
|
|
|
|
|
|
// Items are separated into their individual trader packages, now we can ensure that they all have valid parents
|
|
|
|
this.adoptOrphanedInsEquipment(sessionID);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-04-24 14:03:41 +01:00
|
|
|
/**
|
2023-08-05 17:26:16 +00:00
|
|
|
* Take preraid item and update properties to ensure its ready to be given to player in insurance return mail
|
|
|
|
* @param pmcData Player profile
|
2024-01-08 21:16:53 +00:00
|
|
|
* @param preRaidItemWithChildren Insured item (with children) as it was pre-raid
|
2024-01-23 20:19:53 +00:00
|
|
|
* @param allItemsFromClient Item data when player left raid (durability values)
|
2024-01-08 21:16:53 +00:00
|
|
|
* @returns Item (with children) to send to player
|
2023-04-24 14:03:41 +01:00
|
|
|
*/
|
2023-11-13 11:13:25 -05:00
|
|
|
protected getInsuredItemDetails(
|
|
|
|
pmcData: IPmcData,
|
2024-02-04 22:41:11 +00:00
|
|
|
preRaidItem: Item,
|
|
|
|
insuredItemFromClient: IInsuredItemsData,
|
|
|
|
): Item
|
2023-04-24 14:03:41 +01:00
|
|
|
{
|
2024-02-04 22:41:11 +00:00
|
|
|
// Get baseline item to return, clone pre raid item
|
2024-05-13 17:58:17 +00:00
|
|
|
const itemToReturnClone: Item = this.cloner.clone(preRaidItem);
|
2024-01-08 21:16:53 +00:00
|
|
|
|
2024-02-04 22:41:11 +00:00
|
|
|
// Add upd if it doesnt exist
|
2024-03-07 09:18:39 +00:00
|
|
|
this.itemHelper.addUpdObjectToItem(itemToReturnClone);
|
2024-01-08 21:16:53 +00:00
|
|
|
|
2024-02-04 22:41:11 +00:00
|
|
|
// Check for slotid values that need to be updated and adjust
|
2024-03-07 09:18:39 +00:00
|
|
|
this.updateSlotIdValue(pmcData.Inventory.equipment, itemToReturnClone);
|
2024-01-08 21:16:53 +00:00
|
|
|
|
2024-02-04 22:41:11 +00:00
|
|
|
// Remove location property
|
2024-03-07 09:18:39 +00:00
|
|
|
if (itemToReturnClone.slotId === "hideout" && "location" in itemToReturnClone)
|
2024-02-04 22:41:11 +00:00
|
|
|
{
|
2024-03-07 09:18:39 +00:00
|
|
|
delete itemToReturnClone.location;
|
2024-02-04 22:41:11 +00:00
|
|
|
}
|
2024-01-08 21:16:53 +00:00
|
|
|
|
2024-02-04 22:41:11 +00:00
|
|
|
// Remove found in raid status when upd exists + SpawnedInSession value exists
|
2024-03-07 09:18:39 +00:00
|
|
|
if ("SpawnedInSession" in itemToReturnClone.upd)
|
2024-02-04 22:41:11 +00:00
|
|
|
{
|
2024-03-07 09:18:39 +00:00
|
|
|
itemToReturnClone.upd.SpawnedInSession = false;
|
2024-02-04 22:41:11 +00:00
|
|
|
}
|
2024-01-08 21:16:53 +00:00
|
|
|
|
2024-02-04 22:41:11 +00:00
|
|
|
// Client item has durability values, Ensure values persist into server data
|
|
|
|
if (insuredItemFromClient?.durability)
|
|
|
|
{
|
|
|
|
// Item didnt have Repairable object pre-raid, add it
|
2024-03-07 09:18:39 +00:00
|
|
|
if (!itemToReturnClone.upd.Repairable)
|
2024-02-04 22:41:11 +00:00
|
|
|
{
|
2024-03-07 09:18:39 +00:00
|
|
|
itemToReturnClone.upd.Repairable = {
|
2024-02-04 22:41:11 +00:00
|
|
|
Durability: insuredItemFromClient.durability,
|
|
|
|
MaxDurability: insuredItemFromClient.maxDurability,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-03-07 09:18:39 +00:00
|
|
|
itemToReturnClone.upd.Repairable.Durability = insuredItemFromClient.durability;
|
|
|
|
itemToReturnClone.upd.Repairable.MaxDurability = insuredItemFromClient.maxDurability;
|
2024-02-04 22:41:11 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-08 21:16:53 +00:00
|
|
|
|
2024-02-04 22:41:11 +00:00
|
|
|
// Client item has FaceShield values, Ensure values persist into server data
|
|
|
|
if (insuredItemFromClient?.hits)
|
|
|
|
{
|
|
|
|
// Item didnt have faceshield object pre-raid, add it
|
2024-03-07 09:18:39 +00:00
|
|
|
if (!itemToReturnClone.upd.FaceShield)
|
2024-02-04 22:41:11 +00:00
|
|
|
{
|
2024-03-07 09:18:39 +00:00
|
|
|
itemToReturnClone.upd.FaceShield = { Hits: insuredItemFromClient.hits };
|
2024-02-04 22:41:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-03-07 09:18:39 +00:00
|
|
|
itemToReturnClone.upd.FaceShield.Hits = insuredItemFromClient.hits;
|
2023-08-05 17:26:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-07 09:18:39 +00:00
|
|
|
return itemToReturnClone;
|
2023-04-24 14:03:41 +01:00
|
|
|
}
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
/**
|
2023-08-05 17:26:16 +00:00
|
|
|
* 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
|
2023-03-03 15:23:46 +00:00
|
|
|
*/
|
2023-08-05 17:26:16 +00:00
|
|
|
protected updateSlotIdValue(playerBaseInventoryEquipmentId: string, itemToReturn: Item): void
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-11-13 12:31:52 -05:00
|
|
|
const pocketSlots = ["pocket1", "pocket2", "pocket3", "pocket4"];
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-08-05 17:26:16 +00:00
|
|
|
// Some pockets can lose items with player death, some don't
|
2023-11-13 11:13:25 -05:00
|
|
|
if (!("slotId" in itemToReturn) || pocketSlots.includes(itemToReturn.slotId))
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-08-05 17:26:16 +00:00
|
|
|
itemToReturn.slotId = "hideout";
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-08-05 17:26:16 +00:00
|
|
|
// Mark root-level items for later processing
|
2023-11-13 11:13:25 -05:00
|
|
|
if (itemToReturn.parentId === playerBaseInventoryEquipmentId)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-08-05 17:26:16 +00:00
|
|
|
itemToReturn.slotId = "hideout";
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
2023-08-05 17:26:16 +00:00
|
|
|
}
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-08-05 17:26:16 +00:00
|
|
|
/**
|
|
|
|
* Add gear item to InsuredItems array in player profile
|
|
|
|
* @param sessionID Session id
|
|
|
|
* @param pmcData Player profile
|
|
|
|
* @param itemToReturnToPlayer item to store
|
|
|
|
* @param traderId Id of trader item was insured with
|
|
|
|
*/
|
2024-02-08 15:56:45 -05:00
|
|
|
protected addGearToSend(gear: IInsuranceEquipmentPkg): void
|
2023-08-05 17:26:16 +00:00
|
|
|
{
|
|
|
|
const sessionId = gear.sessionID;
|
|
|
|
const pmcData = gear.pmcData;
|
2024-02-04 22:41:11 +00:00
|
|
|
const itemToReturnToPlayer = gear.itemToReturnToPlayer;
|
2023-08-05 17:26:16 +00:00
|
|
|
const traderId = gear.traderId;
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-08-05 17:26:16 +00:00
|
|
|
// Ensure insurance array is init
|
|
|
|
if (!this.insuranceExists(sessionId))
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-08-05 17:26:16 +00:00
|
|
|
this.resetInsurance(sessionId);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-08-05 17:26:16 +00:00
|
|
|
// init trader insurance array
|
|
|
|
if (!this.insuranceTraderArrayExists(sessionId, traderId))
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
2023-08-05 17:26:16 +00:00
|
|
|
this.resetInsuranceTraderArray(sessionId, traderId);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2024-02-04 22:41:11 +00:00
|
|
|
this.addInsuranceItemToArray(sessionId, traderId, itemToReturnToPlayer);
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-08-05 17:26:16 +00:00
|
|
|
// Remove item from insured items array as its been processed
|
2024-02-04 22:41:11 +00:00
|
|
|
pmcData.InsuredItems = pmcData.InsuredItems.filter((item) =>
|
|
|
|
{
|
|
|
|
return item.itemId !== itemToReturnToPlayer._id;
|
|
|
|
});
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-07-20 16:04:26 +01:00
|
|
|
/**
|
|
|
|
* Does insurance exist for a player and by trader
|
|
|
|
* @param sessionId Player id (session id)
|
|
|
|
* @param traderId Trader items insured with
|
|
|
|
* @returns True if exists
|
|
|
|
*/
|
|
|
|
protected insuranceTraderArrayExists(sessionId: string, traderId: string): boolean
|
|
|
|
{
|
|
|
|
return this.insured[sessionId][traderId] !== undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Empty out array holding insured items by sessionid + traderid
|
|
|
|
* @param sessionId Player id (session id)
|
|
|
|
* @param traderId Trader items insured with
|
|
|
|
*/
|
|
|
|
public resetInsuranceTraderArray(sessionId: string, traderId: string): void
|
|
|
|
{
|
|
|
|
this.insured[sessionId][traderId] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store insured item
|
|
|
|
* @param sessionId Player id (session id)
|
|
|
|
* @param traderId Trader item insured with
|
2024-02-04 22:41:11 +00:00
|
|
|
* @param itemToAdd Insured item (with children)
|
2023-07-20 16:04:26 +01:00
|
|
|
*/
|
2024-02-04 22:41:11 +00:00
|
|
|
public addInsuranceItemToArray(sessionId: string, traderId: string, itemToAdd: Item): void
|
2023-07-20 16:04:26 +01:00
|
|
|
{
|
2024-02-04 22:41:11 +00:00
|
|
|
this.insured[sessionId][traderId].push(itemToAdd);
|
2023-07-20 16:04:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get price of insurance * multiplier from config
|
|
|
|
* @param pmcData Player profile
|
|
|
|
* @param inventoryItem Item to be insured
|
|
|
|
* @param traderId Trader item is insured with
|
|
|
|
* @returns price in roubles
|
|
|
|
*/
|
2023-03-03 15:23:46 +00:00
|
|
|
public getPremium(pmcData: IPmcData, inventoryItem: Item, traderId: string): number
|
|
|
|
{
|
|
|
|
let insuranceMultiplier = this.insuranceConfig.insuranceMultiplier[traderId];
|
|
|
|
if (!insuranceMultiplier)
|
|
|
|
{
|
|
|
|
insuranceMultiplier = 0.3;
|
2023-11-13 11:13:25 -05:00
|
|
|
this.logger.warning(
|
|
|
|
this.localisationService.getText("insurance-missing_insurance_price_multiplier", traderId),
|
|
|
|
);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-07-20 16:04:26 +01:00
|
|
|
// Multiply item handbook price by multiplier in config to get the new insurance price
|
|
|
|
let pricePremium = this.itemHelper.getStaticItemPrice(inventoryItem._tpl) * insuranceMultiplier;
|
2023-03-03 15:23:46 +00:00
|
|
|
const coef = this.traderHelper.getLoyaltyLevel(traderId, pmcData).insurance_price_coef;
|
|
|
|
|
|
|
|
if (coef > 0)
|
|
|
|
{
|
2023-11-13 11:13:25 -05:00
|
|
|
pricePremium *= 1 - this.traderHelper.getLoyaltyLevel(traderId, pmcData).insurance_price_coef / 100;
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-07-20 16:04:26 +01:00
|
|
|
return Math.round(pricePremium);
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
2024-02-08 15:56:45 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the ID that should be used for a root-level Item's parentId property value within in the context of insurance.
|
|
|
|
*
|
|
|
|
* @returns The ID.
|
|
|
|
*/
|
|
|
|
public getRootItemParentID(sessionID: string): string
|
|
|
|
{
|
|
|
|
// Try to use the equipment id from the profile. I'm not sure this is strictly required, but it feels neat.
|
|
|
|
return this.saveServer.getProfile(sessionID)?.characters?.pmc?.Inventory?.equipment ?? this.hashUtil.generate();
|
|
|
|
}
|
2023-11-13 11:13:25 -05:00
|
|
|
}
|