mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-13 09:50:43 -05:00
- Changes to import typing for ILogger and ICloner - Targets es2022 in compilerOptions (Matching the target in .swcrc) because otherwise Biome doesn't seem to find a proper target. - Fixes up the seasonal event typing - fixes run:debug to target the correct file
259 lines
12 KiB
TypeScript
259 lines
12 KiB
TypeScript
import { ApplicationContext } from "@spt/context/ApplicationContext";
|
|
import { LocationLootGenerator } from "@spt/generators/LocationLootGenerator";
|
|
import { LootGenerator } from "@spt/generators/LootGenerator";
|
|
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
|
|
import { HealthHelper } from "@spt/helpers/HealthHelper";
|
|
import { InRaidHelper } from "@spt/helpers/InRaidHelper";
|
|
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
|
import { TraderHelper } from "@spt/helpers/TraderHelper";
|
|
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
|
import { IItem } from "@spt/models/eft/common/tables/IItem";
|
|
import { IEndOfflineRaidRequestData } from "@spt/models/eft/match/IEndOfflineRaidRequestData";
|
|
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
|
import { MessageType } from "@spt/models/enums/MessageType";
|
|
import { Traders } from "@spt/models/enums/Traders";
|
|
import { IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig";
|
|
import { IInRaidConfig } from "@spt/models/spt/config/IInRaidConfig";
|
|
import { ILocationConfig } from "@spt/models/spt/config/ILocationConfig";
|
|
import { IMatchConfig } from "@spt/models/spt/config/IMatchConfig";
|
|
import { IRagfairConfig } from "@spt/models/spt/config/IRagfairConfig";
|
|
import { ITraderConfig } from "@spt/models/spt/config/ITraderConfig";
|
|
import type { ILogger } from "@spt/models/spt/utils/ILogger";
|
|
import { ConfigServer } from "@spt/servers/ConfigServer";
|
|
import { SaveServer } from "@spt/servers/SaveServer";
|
|
import { BotGenerationCacheService } from "@spt/services/BotGenerationCacheService";
|
|
import { BotLootCacheService } from "@spt/services/BotLootCacheService";
|
|
import { DatabaseService } from "@spt/services/DatabaseService";
|
|
import { InsuranceService } from "@spt/services/InsuranceService";
|
|
import { LocalisationService } from "@spt/services/LocalisationService";
|
|
import { MailSendService } from "@spt/services/MailSendService";
|
|
import { MatchBotDetailsCacheService } from "@spt/services/MatchBotDetailsCacheService";
|
|
import { PmcChatResponseService } from "@spt/services/PmcChatResponseService";
|
|
import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService";
|
|
import { HashUtil } from "@spt/utils/HashUtil";
|
|
import { RandomUtil } from "@spt/utils/RandomUtil";
|
|
import { TimeUtil } from "@spt/utils/TimeUtil";
|
|
import type { ICloner } from "@spt/utils/cloners/ICloner";
|
|
import { inject } from "tsyringe";
|
|
|
|
export class LegacyLocationLifecycleService {
|
|
protected matchConfig: IMatchConfig;
|
|
protected inRaidConfig: IInRaidConfig;
|
|
protected traderConfig: ITraderConfig;
|
|
protected ragfairConfig: IRagfairConfig;
|
|
protected hideoutConfig: IHideoutConfig;
|
|
protected locationConfig: ILocationConfig;
|
|
|
|
constructor(
|
|
@inject("PrimaryLogger") protected logger: ILogger,
|
|
@inject("HashUtil") protected hashUtil: HashUtil,
|
|
@inject("SaveServer") protected saveServer: SaveServer,
|
|
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
|
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
|
@inject("DatabaseService") protected databaseService: DatabaseService,
|
|
@inject("InRaidHelper") protected inRaidHelper: InRaidHelper,
|
|
@inject("HealthHelper") protected healthHelper: HealthHelper,
|
|
@inject("MatchBotDetailsCacheService") protected matchBotDetailsCacheService: MatchBotDetailsCacheService,
|
|
@inject("PmcChatResponseService") protected pmcChatResponseService: PmcChatResponseService,
|
|
@inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator,
|
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
|
@inject("InsuranceService") protected insuranceService: InsuranceService,
|
|
@inject("BotLootCacheService") protected botLootCacheService: BotLootCacheService,
|
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
|
@inject("BotGenerationCacheService") protected botGenerationCacheService: BotGenerationCacheService,
|
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
|
@inject("RaidTimeAdjustmentService") protected raidTimeAdjustmentService: RaidTimeAdjustmentService,
|
|
@inject("LootGenerator") protected lootGenerator: LootGenerator,
|
|
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
|
|
@inject("LocationLootGenerator") protected locationLootGenerator: LocationLootGenerator,
|
|
@inject("PrimaryCloner") protected cloner: ICloner,
|
|
) {
|
|
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
|
|
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
|
|
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
|
|
this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION);
|
|
}
|
|
|
|
/**
|
|
* Handle client/match/offline/end
|
|
* @deprecated
|
|
*/
|
|
public endOfflineRaid(info: IEndOfflineRaidRequestData, sessionId: string): void {
|
|
const pmcData: IPmcData = this.profileHelper.getPmcProfile(sessionId);
|
|
const extractName = info.exitName;
|
|
|
|
// Save time spent in raid
|
|
pmcData.Stats.Eft.TotalInGameTime += info.raidSeconds;
|
|
|
|
// Clean up cached bots now raid is over
|
|
this.botGenerationCacheService.clearStoredBots();
|
|
|
|
// Clear bot loot cache
|
|
this.botLootCacheService.clearCache();
|
|
|
|
if (this.extractWasViaCar(extractName)) {
|
|
this.handleCarExtract(extractName, pmcData, sessionId);
|
|
}
|
|
|
|
if (extractName && this.extractWasViaCoop(extractName) && this.traderConfig.fence.coopExtractGift.sendGift) {
|
|
this.handleCoopExtract(sessionId, pmcData, extractName);
|
|
this.sendCoopTakenFenceMessage(sessionId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle when a player extracts using a car - Add rep to fence
|
|
* @param extractName name of the extract used
|
|
* @param pmcData Player profile
|
|
* @param sessionId Session id
|
|
*/
|
|
protected handleCarExtract(extractName: string, pmcData: IPmcData, sessionId: string): void {
|
|
// Ensure key exists for extract
|
|
if (!(extractName in pmcData.CarExtractCounts)) {
|
|
pmcData.CarExtractCounts[extractName] = 0;
|
|
}
|
|
|
|
// Increment extract count value
|
|
pmcData.CarExtractCounts[extractName] += 1;
|
|
|
|
// Not exact replica of Live behaviour
|
|
// Simplified for now, no real reason to do the whole (unconfirmed) extra 0.01 standing per day regeneration mechanic
|
|
const newFenceStanding = this.getFenceStandingAfterExtract(
|
|
pmcData,
|
|
this.inRaidConfig.carExtractBaseStandingGain,
|
|
pmcData.CarExtractCounts[extractName],
|
|
);
|
|
const fenceId: string = Traders.FENCE;
|
|
pmcData.TradersInfo[fenceId].standing = newFenceStanding;
|
|
|
|
// Check if new standing has leveled up trader
|
|
this.traderHelper.lvlUp(fenceId, pmcData);
|
|
pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1);
|
|
|
|
this.logger.debug(
|
|
`Car extract: ${extractName} used, total times taken: ${pmcData.CarExtractCounts[extractName]}`,
|
|
);
|
|
|
|
// Copy updated fence rep values into scav profile to ensure consistency
|
|
const scavData: IPmcData = this.profileHelper.getScavProfile(sessionId);
|
|
scavData.TradersInfo[fenceId].standing = pmcData.TradersInfo[fenceId].standing;
|
|
scavData.TradersInfo[fenceId].loyaltyLevel = pmcData.TradersInfo[fenceId].loyaltyLevel;
|
|
}
|
|
|
|
/**
|
|
* Get the fence rep gain from using a car or coop extract
|
|
* @param pmcData Profile
|
|
* @param baseGain amount gained for the first extract
|
|
* @param extractCount Number of times extract was taken
|
|
* @returns Fence standing after taking extract
|
|
*/
|
|
protected getFenceStandingAfterExtract(pmcData: IPmcData, baseGain: number, extractCount: number): number {
|
|
// Get current standing
|
|
const fenceId: string = Traders.FENCE;
|
|
let fenceStanding = Number(pmcData.TradersInfo[fenceId].standing);
|
|
|
|
// get standing after taking extract x times, x.xx format, gain from extract can be no smaller than 0.01
|
|
fenceStanding += Math.max(baseGain / extractCount, 0.01);
|
|
|
|
// Ensure fence loyalty level is not above/below the range -7 to 15
|
|
const newFenceStanding = Math.min(Math.max(fenceStanding, -7), 15);
|
|
this.logger.debug(`Old vs new fence standing: ${pmcData.TradersInfo[fenceId].standing}, ${newFenceStanding}`);
|
|
|
|
return Number(newFenceStanding.toFixed(2));
|
|
}
|
|
|
|
/**
|
|
* Was extract by car
|
|
* @param extractName name of extract
|
|
* @returns true if car extract
|
|
*/
|
|
protected extractWasViaCar(extractName: string): boolean {
|
|
// exit name is undefined on death
|
|
if (!extractName) {
|
|
return false;
|
|
}
|
|
|
|
if (extractName.toLowerCase().includes("v-ex")) {
|
|
return true;
|
|
}
|
|
|
|
return this.inRaidConfig.carExtracts.includes(extractName.trim());
|
|
}
|
|
|
|
/**
|
|
* Did player take a COOP extract
|
|
* @param extractName Name of extract player took
|
|
* @returns True if coop extract
|
|
*/
|
|
protected extractWasViaCoop(extractName: string): boolean {
|
|
// No extract name, not a coop extract
|
|
if (!extractName) {
|
|
return false;
|
|
}
|
|
|
|
return this.inRaidConfig.coopExtracts.includes(extractName.trim());
|
|
}
|
|
|
|
/**
|
|
* Handle when a player extracts using a coop extract - add rep to fence
|
|
* @param sessionId Session/player id
|
|
* @param pmcData Profile
|
|
* @param extractName Name of extract taken
|
|
*/
|
|
protected handleCoopExtract(sessionId: string, pmcData: IPmcData, extractName: string): void {
|
|
if (!pmcData.CoopExtractCounts) {
|
|
pmcData.CoopExtractCounts = {};
|
|
}
|
|
|
|
// Ensure key exists for extract
|
|
if (!(extractName in pmcData.CoopExtractCounts)) {
|
|
pmcData.CoopExtractCounts[extractName] = 0;
|
|
}
|
|
|
|
// Increment extract count value
|
|
pmcData.CoopExtractCounts[extractName] += 1;
|
|
|
|
// Get new fence standing value
|
|
const newFenceStanding = this.getFenceStandingAfterExtract(
|
|
pmcData,
|
|
this.inRaidConfig.coopExtractBaseStandingGain,
|
|
pmcData.CoopExtractCounts[extractName],
|
|
);
|
|
const fenceId: string = Traders.FENCE;
|
|
pmcData.TradersInfo[fenceId].standing = newFenceStanding;
|
|
|
|
// Check if new standing has leveled up trader
|
|
this.traderHelper.lvlUp(fenceId, pmcData);
|
|
pmcData.TradersInfo[fenceId].loyaltyLevel = Math.max(pmcData.TradersInfo[fenceId].loyaltyLevel, 1);
|
|
|
|
// Copy updated fence rep values into scav profile to ensure consistency
|
|
const scavData: IPmcData = this.profileHelper.getScavProfile(sessionId);
|
|
scavData.TradersInfo[fenceId].standing = pmcData.TradersInfo[fenceId].standing;
|
|
scavData.TradersInfo[fenceId].loyaltyLevel = pmcData.TradersInfo[fenceId].loyaltyLevel;
|
|
}
|
|
|
|
protected sendCoopTakenFenceMessage(sessionId: string): void {
|
|
// Generate reward for taking coop extract
|
|
const loot = this.lootGenerator.createRandomLoot(this.traderConfig.fence.coopExtractGift);
|
|
const mailableLoot: IItem[] = [];
|
|
|
|
const parentId = this.hashUtil.generate();
|
|
for (const item of loot) {
|
|
item.parentId = parentId;
|
|
mailableLoot.push(item);
|
|
}
|
|
|
|
// Send message from fence giving player reward generated above
|
|
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
|
sessionId,
|
|
this.traderHelper.getTraderById(Traders.FENCE),
|
|
MessageType.MESSAGE_WITH_ITEMS,
|
|
this.randomUtil.getArrayValue(this.traderConfig.fence.coopExtractGift.messageLocaleIds),
|
|
mailableLoot,
|
|
this.timeUtil.getHoursAsSeconds(this.traderConfig.fence.coopExtractGift.giftExpiryHours),
|
|
);
|
|
}
|
|
}
|