mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-13 09:50:43 -05:00
Notable coding Changes: - Added `getRootItemParentID` method in `InsuranceService` to standardize the determination of the root insurance container. - Added `IInsuranceEquipmentPkg` model for structuring insurance packages, a type used to store insurance item data before it's saved in the profile. - Added `HashUtil` in `InsuranceController` and `InsuranceService` for generating an ID for the root insurance container in the case that the root ID cannot be found. - Updated and normalized item map generation and usage across `InsuranceService` and `InsuranceController`. - Updated `ItemHelper` with new methods `adoptOrphanedItems` and `generateItemsMap`, facilitating better management of item relationships and efficient item look-ups. - Updated `InsuranceController.findItemsToDelete` and related methods to use the new `rootItemParentID` parameter to ensure that all root level items share the same parent ID. - Updated logic in `InsuranceService` for creating insurance packages and handling orphaned items. Uh-huh, but what would you say you do here? - Resolves an issue that arose when `lostondeath.json` equipment configuration options were set to `false`. On death, the equipment's children items would be sent back to the player through insurance, duplicating them. - Resolves an issue that prevented items from appearing in an insurance return even though they passed an insurance roll. - Improved debug logging. Remaining Oopses: - 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. Resolves #425
560 lines
23 KiB
TypeScript
560 lines
23 KiB
TypeScript
import { inject, injectable } from "tsyringe";
|
|
|
|
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";
|
|
import { BonusType } from "@spt-aki/models/enums/BonusType";
|
|
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
|
import { MessageType } from "@spt-aki/models/enums/MessageType";
|
|
import { Traders } from "@spt-aki/models/enums/Traders";
|
|
import { IInsuranceConfig } from "@spt-aki/models/spt/config/IInsuranceConfig";
|
|
import { ILostOnDeathConfig } from "@spt-aki/models/spt/config/ILostOnDeathConfig";
|
|
import { IInsuranceEquipmentPkg } from "@spt-aki/models/spt/services/IInsuranceEquipmentPkg";
|
|
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";
|
|
import { HashUtil } from "@spt-aki/utils/HashUtil";
|
|
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
|
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
|
|
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
|
|
|
|
@injectable()
|
|
export class InsuranceService
|
|
{
|
|
protected insured: Record<string, Record<string, Item[]>> = {};
|
|
protected insuranceConfig: IInsuranceConfig;
|
|
protected lostOnDeathConfig: ILostOnDeathConfig;
|
|
|
|
constructor(
|
|
@inject("WinstonLogger") protected logger: ILogger,
|
|
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
|
@inject("SecureContainerHelper") protected secureContainerHelper: SecureContainerHelper,
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
|
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
|
@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,
|
|
@inject("LocaleService") protected localeService: LocaleService,
|
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
|
)
|
|
{
|
|
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
|
|
this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH);
|
|
}
|
|
|
|
/**
|
|
* Does player have insurance array
|
|
* @param sessionId Player id
|
|
* @returns True if exists
|
|
*/
|
|
public insuranceExists(sessionId: string): boolean
|
|
{
|
|
return this.insured[sessionId] !== undefined;
|
|
}
|
|
|
|
/**
|
|
* Get all insured items by all traders for a profile
|
|
* @param sessionId Profile id (session id)
|
|
* @returns Item array
|
|
*/
|
|
public getInsurance(sessionId: string): Record<string, Item[]>
|
|
{
|
|
return this.insured[sessionId];
|
|
}
|
|
|
|
/**
|
|
* Get insured items by profile id + trader id
|
|
* @param sessionId Profile id (session id)
|
|
* @param traderId Trader items were insured with
|
|
* @returns Item array
|
|
*/
|
|
public getInsuranceItems(sessionId: string, traderId: string): Item[]
|
|
{
|
|
return this.insured[sessionId][traderId];
|
|
}
|
|
|
|
public resetInsurance(sessionId: string): void
|
|
{
|
|
this.insured[sessionId] = {};
|
|
}
|
|
|
|
/**
|
|
* Sends stored insured items as message to player
|
|
* @param pmcData profile to send insured items to
|
|
* @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
|
|
{
|
|
// Get insurance items for each trader
|
|
for (const traderId in this.getInsurance(sessionID))
|
|
{
|
|
const traderBase = this.traderHelper.getTrader(traderId, sessionID);
|
|
const insuranceReturnTimestamp = this.getInsuranceReturnTimestamp(pmcData, traderBase);
|
|
const dialogueTemplates = this.databaseServer.getTables().traders[traderId].dialogue;
|
|
|
|
const systemData = {
|
|
date: this.timeUtil.getDateMailFormat(),
|
|
time: this.timeUtil.getTimeMailFormat(),
|
|
location: mapId,
|
|
};
|
|
// 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,
|
|
null,
|
|
systemData,
|
|
);
|
|
|
|
// 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
|
|
this.saveServer.getProfile(sessionID).insurance.push({
|
|
scheduledTime: insuranceReturnTimestamp,
|
|
traderId: traderId,
|
|
maxStorageTime: this.timeUtil.getHoursAsSeconds(traderBase.insurance.max_storage_time),
|
|
systemData: systemData,
|
|
messageType: MessageType.INSURANCE_RETURN,
|
|
messageTemplateId: this.randomUtil.getArrayValue(dialogueTemplates.insuranceFound),
|
|
items: this.getInsurance(sessionID)[traderId],
|
|
});
|
|
}
|
|
|
|
this.resetInsurance(sessionID);
|
|
}
|
|
|
|
/**
|
|
* Send a message to player informing them gear was completely lost
|
|
* @param sessionId Session id
|
|
* @param locationName name of map insurance was lost on
|
|
*/
|
|
public sendLostInsuranceMessage(sessionId: string, locationName = ""): void
|
|
{
|
|
const dialogueTemplates = this.databaseServer.getTables().traders[Traders.PRAPOR].dialogue; // todo: get trader id instead of hard coded prapor
|
|
const randomResponseId = locationName?.toLowerCase() === "laboratory"
|
|
? this.randomUtil.getArrayValue(dialogueTemplates.insuranceFailedLabs)
|
|
: this.randomUtil.getArrayValue(dialogueTemplates.insuranceFailed);
|
|
|
|
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
|
sessionId,
|
|
this.traderHelper.getTraderById(Traders.PRAPOR),
|
|
MessageType.NPC_TRADER,
|
|
randomResponseId,
|
|
[],
|
|
null,
|
|
{ location: locationName },
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check all root insured items and remove location property + set slotId to 'hideout'
|
|
* @param sessionId Session id
|
|
* @param traderId Trader id
|
|
*/
|
|
protected removeLocationProperty(sessionId: string, traderId: string): void
|
|
{
|
|
const insuredItems = this.getInsurance(sessionId)[traderId];
|
|
for (const insuredItem of this.getInsurance(sessionId)[traderId])
|
|
{
|
|
// Find insured items parent
|
|
const insuredItemsParent = insuredItems.find((x) => x._id === insuredItem.parentId);
|
|
if (!insuredItemsParent)
|
|
{
|
|
// Remove location + set slotId of insured items parent
|
|
insuredItem.slotId = "hideout";
|
|
delete insuredItem.location;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* @param pmcData Player profile
|
|
* @param trader Trader base used to insure items
|
|
* @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)
|
|
{
|
|
this.logger.debug(
|
|
`Insurance override used: returning in ${this.insuranceConfig.returnTimeOverrideSeconds} seconds`,
|
|
);
|
|
return this.timeUtil.getTimestamp() + this.insuranceConfig.returnTimeOverrideSeconds;
|
|
}
|
|
|
|
const insuranceReturnTimeBonus = pmcData.Bonuses.find((b) => b.type === BonusType.INSURANCE_RETURN_TIME);
|
|
const insuranceReturnTimeBonusPercent = 1.0
|
|
- (insuranceReturnTimeBonus ? Math.abs(insuranceReturnTimeBonus.value) : 0) / 100;
|
|
|
|
const traderMinReturnAsSeconds = trader.insurance.min_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS;
|
|
const traderMaxReturnAsSeconds = trader.insurance.max_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS;
|
|
const randomisedReturnTimeSeconds = this.randomUtil.getInt(traderMinReturnAsSeconds, traderMaxReturnAsSeconds);
|
|
|
|
// Current time + randomised time calculated above
|
|
return this.timeUtil.getTimestamp() + (randomisedReturnTimeSeconds * insuranceReturnTimeBonusPercent);
|
|
}
|
|
|
|
/**
|
|
* 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. >:{}
|
|
*
|
|
* @param pmcData Player profile
|
|
* @param offraidData Post-raid data
|
|
* @param preRaidGear Pre-raid data
|
|
* @param sessionID Session id
|
|
* @param playerDied Did player die in raid
|
|
* @returns Array of insured items lost in raid
|
|
*/
|
|
public getGearLostInRaid(
|
|
pmcData: IPmcData,
|
|
offraidData: ISaveProgressRequestData,
|
|
preRaidGear: Item[],
|
|
sessionID: string,
|
|
playerDied: boolean,
|
|
): IInsuranceEquipmentPkg[]
|
|
{
|
|
const equipmentPkg: IInsuranceEquipmentPkg[] = [];
|
|
const preRaidGearMap = this.itemHelper.generateItemsMap(preRaidGear);
|
|
const offRaidGearMap = this.itemHelper.generateItemsMap(offraidData.profile.Inventory.items);
|
|
|
|
for (const insuredItem of pmcData.InsuredItems)
|
|
{
|
|
// Skip insured items not on player when they started the raid.
|
|
if (!preRaidGearMap.has(insuredItem.itemId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const preRaidItem = preRaidGearMap.get(insuredItem.itemId);
|
|
|
|
// Skip slots we should never return as they're never lost on death
|
|
if (this.insuranceConfig.blacklistedEquipment.includes(preRaidItem.slotId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// 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;
|
|
|
|
// Was the item found in the player inventory post-raid?
|
|
const itemOnPlayerPostRaid = offRaidGearMap.has(insuredItem.itemId);
|
|
|
|
// Check if item missing in post-raid gear OR player died + item slot flagged as lost on death
|
|
// Catches both events: player died with item on + player survived but dropped item in raid
|
|
if (!itemOnPlayerPostRaid || (playerDied && itemShouldBeLostOnDeath))
|
|
{
|
|
equipmentPkg.push({
|
|
pmcData: pmcData,
|
|
itemToReturnToPlayer: this.getInsuredItemDetails(
|
|
pmcData,
|
|
preRaidItem,
|
|
offraidData.insurance?.find((insuranceItem) => insuranceItem.id === insuredItem.itemId),
|
|
),
|
|
traderId: insuredItem.tid,
|
|
sessionID: sessionID,
|
|
});
|
|
|
|
// 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
|
|
const softInsertChildIds = preRaidGear.filter((item) =>
|
|
item.parentId === preRaidItem._id
|
|
&& this.itemHelper.getSoftInsertSlotIds().includes(item.slotId.toLowerCase())
|
|
).map((x) => x._id);
|
|
|
|
// Add all items found above to return data
|
|
for (const softInsertChildModId of softInsertChildIds)
|
|
{
|
|
equipmentPkg.push({
|
|
pmcData: pmcData,
|
|
itemToReturnToPlayer: this.getInsuredItemDetails(
|
|
pmcData,
|
|
preRaidGear.find((item) => item._id === softInsertChildModId),
|
|
offraidData.insurance?.find((insuranceItem) =>
|
|
insuranceItem.id === softInsertChildModId
|
|
),
|
|
),
|
|
traderId: insuredItem.tid,
|
|
sessionID: sessionID,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Store lost gear post-raid inside profile, ready for later code to pick it up and mail it
|
|
* @param equipmentPkg Gear to store - generated by getGearLostInRaid()
|
|
*/
|
|
public storeGearLostInRaidToSendLater(sessionID: string, equipmentPkg: IInsuranceEquipmentPkg[]): void
|
|
{
|
|
// Process all insured items lost in-raid
|
|
for (const gear of equipmentPkg)
|
|
{
|
|
this.addGearToSend(gear);
|
|
}
|
|
|
|
// Items are separated into their individual trader packages, now we can ensure that they all have valid parents
|
|
this.adoptOrphanedInsEquipment(sessionID);
|
|
}
|
|
|
|
/**
|
|
* Take preraid item and update properties to ensure its ready to be given to player in insurance return mail
|
|
* @param pmcData Player profile
|
|
* @param preRaidItemWithChildren Insured item (with children) as it was pre-raid
|
|
* @param allItemsFromClient Item data when player left raid (durability values)
|
|
* @returns Item (with children) to send to player
|
|
*/
|
|
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";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
protected addGearToSend(gear: IInsuranceEquipmentPkg): void
|
|
{
|
|
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))
|
|
{
|
|
this.resetInsurance(sessionId);
|
|
}
|
|
|
|
// init trader insurance array
|
|
if (!this.insuranceTraderArrayExists(sessionId, traderId))
|
|
{
|
|
this.resetInsuranceTraderArray(sessionId, traderId);
|
|
}
|
|
|
|
this.addInsuranceItemToArray(sessionId, traderId, itemToReturnToPlayer);
|
|
|
|
// Remove item from insured items array as its been processed
|
|
pmcData.InsuredItems = pmcData.InsuredItems.filter((item) =>
|
|
{
|
|
return item.itemId !== itemToReturnToPlayer._id;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* @param itemToAdd Insured item (with children)
|
|
*/
|
|
public addInsuranceItemToArray(sessionId: string, traderId: string, itemToAdd: Item): void
|
|
{
|
|
this.insured[sessionId][traderId].push(itemToAdd);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
public getPremium(pmcData: IPmcData, inventoryItem: Item, traderId: string): number
|
|
{
|
|
let insuranceMultiplier = this.insuranceConfig.insuranceMultiplier[traderId];
|
|
if (!insuranceMultiplier)
|
|
{
|
|
insuranceMultiplier = 0.3;
|
|
this.logger.warning(
|
|
this.localisationService.getText("insurance-missing_insurance_price_multiplier", traderId),
|
|
);
|
|
}
|
|
|
|
// Multiply item handbook price by multiplier in config to get the new insurance price
|
|
let pricePremium = this.itemHelper.getStaticItemPrice(inventoryItem._tpl) * insuranceMultiplier;
|
|
const coef = this.traderHelper.getLoyaltyLevel(traderId, pmcData).insurance_price_coef;
|
|
|
|
if (coef > 0)
|
|
{
|
|
pricePremium *= 1 - this.traderHelper.getLoyaltyLevel(traderId, pmcData).insurance_price_coef / 100;
|
|
}
|
|
|
|
return Math.round(pricePremium);
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
}
|