0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-13 01:50:44 -05:00

Wipe / Prestiging changes (#1106)

- Full wipe character on unlocking prestige, this will force player to
run through the player setup menu again (This now mirrors live)
- Wiping a profile now keeps the achievements, prestige level and total
time played (This now mirrors live)
- Wiping or prestiging now re-unlocks achievements as intended
- Stops `addAchievementToProfile` from adding with a new time if an
achievement is already added.
This commit is contained in:
Jesse 2025-02-11 21:29:39 +01:00 committed by GitHub
parent 96f5e0a6a2
commit 89fd1c95a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 239 additions and 187 deletions

View File

@ -1,55 +1,17 @@
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
import { DialogueHelper } from "@spt/helpers/DialogueHelper";
import { InventoryHelper } from "@spt/helpers/InventoryHelper";
import type { ItemHelper } from "@spt/helpers/ItemHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { QuestHelper } from "@spt/helpers/QuestHelper";
import { TraderHelper } from "@spt/helpers/TraderHelper";
import { CustomisationSource } from "@spt/models/eft/common/tables/ICustomisationStorage";
import { IPrestige } from "@spt/models/eft/common/tables/IPrestige";
import { IReward } from "@spt/models/eft/common/tables/IReward";
import { IAddItemDirectRequest } from "@spt/models/eft/inventory/IAddItemDirectRequest";
import { IAddItemsDirectRequest } from "@spt/models/eft/inventory/IAddItemsDirectRequest";
import { IObtainPrestigeRequest } from "@spt/models/eft/prestige/IObtainPrestigeRequest";
import { IProfileCreateRequestData } from "@spt/models/eft/profile/IProfileCreateRequestData";
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
import { SkillTypes } from "@spt/models/enums/SkillTypes";
import type { ILogger } from "@spt/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
import { IPendingPrestige } from "@spt/models/eft/profile/ISptProfile";
import { SaveServer } from "@spt/servers/SaveServer";
import { CreateProfileService } from "@spt/services/CreateProfileService";
import { DatabaseService } from "@spt/services/DatabaseService";
import { LocalisationService } from "@spt/services/LocalisationService";
import { MailSendService } from "@spt/services/MailSendService";
import { ProfileFixerService } from "@spt/services/ProfileFixerService";
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { HashUtil } from "@spt/utils/HashUtil";
import { TimeUtil } from "@spt/utils/TimeUtil";
import type { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
import type { IEmptyRequestData } from "../models/eft/common/IEmptyRequestData";
@injectable()
export class PrestigeController {
constructor(
@inject("PrimaryLogger") protected logger: ILogger,
@inject("HashUtil") protected hashUtil: HashUtil,
@inject("PrimaryCloner") protected cloner: ICloner,
@inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("SaveServer") protected saveServer: SaveServer,
@inject("DatabaseService") protected databaseService: DatabaseService,
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("ProfileFixerService") protected profileFixerService: ProfileFixerService,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("CreateProfileService") protected createProfileService: CreateProfileService,
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("DialogueHelper") protected dialogueHelper: DialogueHelper,
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
@inject("QuestHelper") protected questHelper: QuestHelper,
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
) {}
@ -65,14 +27,12 @@ export class PrestigeController {
*/
public async obtainPrestige(sessionId: string, request: IObtainPrestigeRequest[]): Promise<void> {
// Going to prestige 1
// transfer
// 5% of skills should be transfered over
// 5% of mastering should be transfered over
// earned achievements should be transfered over
// profile stats should be transfered over
// prestige progress should be transfered over
// reset
// trader standing
// task progress
@ -80,142 +40,18 @@ export class PrestigeController {
// stash
// hideout progress
const prePrestigeProfileClone = this.cloner.clone(this.profileHelper.getFullProfile(sessionId));
const prePrestigePmc = prePrestigeProfileClone.characters.pmc;
const createRequest: IProfileCreateRequestData = {
side: prePrestigePmc.Info.Side,
nickname: prePrestigePmc.Info.Nickname,
headId: prePrestigePmc.Customization.Head,
voiceId: Object.values(this.databaseService.getTemplates().customization).find(
(customisation) => customisation._name === prePrestigePmc.Info.Voice,
)._id,
sptForcePrestigeLevel: prePrestigeProfileClone.characters.pmc.Info.PrestigeLevel + 1, // Current + 1,
const profile = this.profileHelper.getFullProfile(sessionId);
if (profile) {
const pendingPrestige: IPendingPrestige = {
prestigeLevel: profile.characters.pmc.Info.PrestigeLevel + 1,
items: request,
};
// Reset profile
await this.createProfileService.createProfile(sessionId, createRequest);
profile.spt.pendingPrestige = pendingPrestige;
profile.info.wipe = true;
// Get freshly reset profile ready for editing
const newProfile = this.profileHelper.getFullProfile(sessionId);
if (!newProfile) {
this.logger.error(`Unable to create get new profile for: ${sessionId}`);
return;
}
// Skill copy
const commonSKillsToCopy = prePrestigePmc.Skills.Common;
for (const skillToCopy of commonSKillsToCopy) {
// Set progress 5% of what it was
skillToCopy.Progress = skillToCopy.Progress * 0.05;
const existingSkill = newProfile.characters.pmc.Skills.Common.find((skill) => skill.Id === skillToCopy.Id);
if (existingSkill) {
existingSkill.Progress = skillToCopy.Progress;
} else {
newProfile.characters.pmc.Skills.Common.push(skillToCopy);
}
}
const masteringSkillsToCopy = prePrestigePmc.Skills.Mastering;
for (const skillToCopy of masteringSkillsToCopy) {
// Set progress 5% of what it was
skillToCopy.Progress = skillToCopy.Progress * 0.05;
const existingSkill = newProfile.characters.pmc.Skills.Mastering.find(
(skill) => skill.Id === skillToCopy.Id,
);
if (existingSkill) {
existingSkill.Progress = skillToCopy.Progress;
} else {
newProfile.characters.pmc.Skills.Mastering.push(skillToCopy);
}
}
const indexOfPrestigeObtained = Math.min(createRequest.sptForcePrestigeLevel - 1, 1); // Index starts at 0
// Add existing completed achievements and new one for prestige
newProfile.characters.pmc.Achievements = prePrestigeProfileClone.characters.pmc.Achievements; // this *should* only contain completed ones
// Add "Prestigious" achievement
if (!newProfile.characters.pmc.Achievements["676091c0f457869a94017a23"]) {
newProfile.characters.pmc.Achievements["676091c0f457869a94017a23"] = this.timeUtil.getTimestamp();
}
// Assumes Prestige data is in descending order
const currentPrestigeData = this.databaseService.getTemplates().prestige.elements[indexOfPrestigeObtained];
const prestigeRewards = this.databaseService
.getTemplates()
.prestige.elements.slice(0, indexOfPrestigeObtained + 1)
.flatMap((prestige) => prestige.rewards);
this.addPrestigeRewardsToProfile(sessionId, newProfile, prestigeRewards);
// Flag profile as having achieved this prestige level
newProfile.characters.pmc.Prestige[currentPrestigeData.id] = this.timeUtil.getTimestamp();
// Copy transferred items
for (const transferRequest of request) {
const item = prePrestigePmc.Inventory.items.find((item) => item._id === transferRequest.id);
if (!item) {
this.logger.error(
`Unable to find item with id: ${transferRequest.id} in profile: ${sessionId}, skipping`,
);
continue;
}
const addItemRequest: IAddItemDirectRequest = {
itemWithModsToAdd: [item],
foundInRaid: item.upd?.SpawnedInSession ?? false,
useSortingTable: false,
};
this.inventoryHelper.addItemToStash(
sessionId,
addItemRequest,
newProfile.characters.pmc,
this.eventOutputHolder.getOutput(sessionId),
);
}
// Force save of above changes to disk
await this.saveServer.saveProfile(sessionId);
}
protected addPrestigeRewardsToProfile(sessionId: string, newProfile: ISptProfile, rewards: IReward[]) {
for (const reward of rewards) {
switch (reward.type) {
case "CustomizationDirect": {
this.profileHelper.addHideoutCustomisationUnlock(newProfile, reward, CustomisationSource.PRESTIGE);
break;
}
case "Skill":
this.profileHelper.addSkillPointsToPlayer(
newProfile.characters.pmc,
reward.target as SkillTypes,
reward.value as number,
);
break;
case "Item": {
const addItemRequest: IAddItemDirectRequest = {
itemWithModsToAdd: reward.items,
foundInRaid: reward.items[0]?.upd?.SpawnedInSession,
useSortingTable: false,
callback: null,
};
this.inventoryHelper.addItemToStash(
sessionId,
addItemRequest,
newProfile.characters.pmc,
this.eventOutputHolder.getOutput(sessionId),
);
break;
}
// case "ExtraDailyQuest": {
// // todo
// break;
// }
default:
this.logger.error(`Unhandled prestige reward type: ${reward.type}`);
break;
}
}
}
}

View File

@ -109,6 +109,7 @@ import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper";
import { NotifierHelper } from "@spt/helpers/NotifierHelper";
import { PaymentHelper } from "@spt/helpers/PaymentHelper";
import { PresetHelper } from "@spt/helpers/PresetHelper";
import { PrestigeHelper } from "@spt/helpers/PrestigeHelper";
import { ProbabilityHelper } from "@spt/helpers/ProbabilityHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { QuestConditionHelper } from "@spt/helpers/QuestConditionHelper";
@ -120,8 +121,8 @@ import { RagfairSellHelper } from "@spt/helpers/RagfairSellHelper";
import { RagfairServerHelper } from "@spt/helpers/RagfairServerHelper";
import { RagfairSortHelper } from "@spt/helpers/RagfairSortHelper";
import { RepairHelper } from "@spt/helpers/RepairHelper";
import { RewardHelper } from "@spt/helpers/RewardHelper";
import { RepeatableQuestHelper } from "@spt/helpers/RepeatableQuestHelper";
import { RewardHelper } from "@spt/helpers/RewardHelper";
import { SecureContainerHelper } from "@spt/helpers/SecureContainerHelper";
import { TradeHelper } from "@spt/helpers/TradeHelper";
import { TraderAssortHelper } from "@spt/helpers/TraderAssortHelper";
@ -636,6 +637,7 @@ export class Container {
});
depContainer.register<BotDifficultyHelper>("BotDifficultyHelper", { useClass: BotDifficultyHelper });
depContainer.register<RepeatableQuestHelper>("RepeatableQuestHelper", { useClass: RepeatableQuestHelper });
depContainer.register<PrestigeHelper>("PrestigeHelper", PrestigeHelper);
// ChatBots
depContainer.register<SptDialogueChatBot>("SptDialogueChatBot", SptDialogueChatBot);

View File

@ -0,0 +1,139 @@
import { CustomisationSource } from "@spt/models/eft/common/tables/ICustomisationStorage";
import { IItem } from "@spt/models/eft/common/tables/IItem";
import { IReward } from "@spt/models/eft/common/tables/IReward";
import { IPendingPrestige, ISptProfile } from "@spt/models/eft/profile/ISptProfile";
import { SkillTypes } from "@spt/models/enums/SkillTypes";
import type { ILogger } from "@spt/models/spt/utils/ILogger";
import { DatabaseService } from "@spt/services/DatabaseService";
import { MailSendService } from "@spt/services/MailSendService";
import { TimeUtil } from "@spt/utils/TimeUtil";
import type { ICloner } from "@spt/utils/cloners/ICloner";
import { inject, injectable } from "tsyringe";
import { ProfileHelper } from "./ProfileHelper";
import { RewardHelper } from "./RewardHelper";
@injectable()
export class PrestigeHelper {
constructor(
@inject("PrimaryLogger") protected logger: ILogger,
@inject("PrimaryCloner") protected cloner: ICloner,
@inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("DatabaseService") protected databaseService: DatabaseService,
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("RewardHelper") protected rewardHelper: RewardHelper,
) {}
public processPendingPrestige(oldProfile: ISptProfile, newProfile: ISptProfile, prestige: IPendingPrestige) {
const prePrestigePmc = oldProfile.characters.pmc;
const sessionId = newProfile.info.id;
// Skill copy
if (prePrestigePmc.Skills.Common) {
const commonSKillsToCopy = prePrestigePmc.Skills.Common;
for (const skillToCopy of commonSKillsToCopy) {
// Set progress 5% of what it was
skillToCopy.Progress = skillToCopy.Progress * 0.05;
const existingSkill = newProfile.characters.pmc.Skills.Common.find(
(skill) => skill.Id === skillToCopy.Id,
);
if (existingSkill) {
existingSkill.Progress = skillToCopy.Progress;
} else {
newProfile.characters.pmc.Skills.Common.push(skillToCopy);
}
}
const masteringSkillsToCopy = prePrestigePmc.Skills.Mastering;
for (const skillToCopy of masteringSkillsToCopy) {
// Set progress 5% of what it was
skillToCopy.Progress = skillToCopy.Progress * 0.05;
const existingSkill = newProfile.characters.pmc.Skills.Mastering.find(
(skill) => skill.Id === skillToCopy.Id,
);
if (existingSkill) {
existingSkill.Progress = skillToCopy.Progress;
} else {
newProfile.characters.pmc.Skills.Mastering.push(skillToCopy);
}
}
}
const indexOfPrestigeObtained = Math.min(prestige.prestigeLevel - 1, 1); // Index starts at 0
// Add "Prestigious" achievement
if (!newProfile.characters.pmc.Achievements["676091c0f457869a94017a23"]) {
this.rewardHelper.addAchievementToProfile(newProfile, "676091c0f457869a94017a23");
}
// Assumes Prestige data is in descending order
const currentPrestigeData = this.databaseService.getTemplates().prestige.elements[indexOfPrestigeObtained];
const prestigeRewards = this.databaseService
.getTemplates()
.prestige.elements.slice(0, indexOfPrestigeObtained + 1)
.flatMap((prestige) => prestige.rewards);
this.addPrestigeRewardsToProfile(sessionId, newProfile, prestigeRewards);
// Flag profile as having achieved this prestige level
newProfile.characters.pmc.Prestige[currentPrestigeData.id] = this.timeUtil.getTimestamp();
const itemsToTransfer: IItem[] = [];
// Copy transferred items
for (const transferRequest of prestige.items ?? []) {
const item = prePrestigePmc.Inventory.items.find((item) => item._id === transferRequest.id);
if (!item) {
this.logger.error(
`Unable to find item with id: ${transferRequest.id} in profile: ${sessionId}, skipping`,
);
continue;
}
itemsToTransfer.push(item);
}
this.mailSendService.sendSystemMessageToPlayer(sessionId, "", itemsToTransfer, 31536000);
newProfile.characters.pmc.Info.PrestigeLevel = prestige.prestigeLevel;
}
protected addPrestigeRewardsToProfile(sessionId: string, newProfile: ISptProfile, rewards: IReward[]) {
const itemsToSend: IItem[] = [];
for (const reward of rewards) {
switch (reward.type) {
case "CustomizationDirect": {
this.profileHelper.addHideoutCustomisationUnlock(newProfile, reward, CustomisationSource.PRESTIGE);
break;
}
case "Skill":
this.profileHelper.addSkillPointsToPlayer(
newProfile.characters.pmc,
reward.target as SkillTypes,
reward.value as number,
);
break;
case "Item": {
if (reward.items) {
itemsToSend.push(...reward.items);
}
break;
}
// case "ExtraDailyQuest": {
// // todo
// break;
// }
default:
this.logger.error(`Unhandled prestige reward type: ${reward.type}`);
break;
}
}
if (itemsToSend.length > 0) {
this.mailSendService.sendSystemMessageToPlayer(sessionId, "", itemsToSend, 31536000);
}
}
}

View File

@ -198,6 +198,7 @@ export class ProfileHelper {
cultistRewards: new Map(),
mods: [],
receivedGifts: [],
pendingPrestige: undefined,
};
}

View File

@ -9,7 +9,6 @@ import { IReward } from "@spt/models/eft/common/tables/IReward";
import { IHideoutProduction } from "@spt/models/eft/hideout/IHideoutProduction";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
import { BaseClasses } from "@spt/models/enums/BaseClasses";
import { RewardType } from "@spt/models/enums/RewardType";
import { SkillTypes } from "@spt/models/enums/SkillTypes";
import type { ILogger } from "@spt/models/spt/utils/ILogger";
@ -348,8 +347,10 @@ export class RewardHelper {
* @param achievementId Id of achievement to add
*/
public addAchievementToProfile(fullProfile: ISptProfile, achievementId: string): void {
if (!fullProfile.characters.pmc.Achievements[achievementId]) {
// Add achievement id to profile with timestamp it was unlocked
fullProfile.characters.pmc.Achievements[achievementId] = this.timeUtil.getTimestamp();
}
// Check for any customisation unlocks
const achievementDataDb = this.databaseService

View File

@ -5,6 +5,7 @@ import { EquipmentBuildType } from "@spt/models/enums/EquipmentBuildType";
import { MemberCategory } from "@spt/models/enums/MemberCategory";
import { MessageType } from "@spt/models/enums/MessageType";
import { IProfileChangeEvent } from "@spt/models/spt/dialog/ISendMessageDetails";
import { IObtainPrestigeRequest } from "../prestige/IObtainPrestigeRequest";
import { ISystemData } from "./ISystemData";
import { IUserDialogInfo } from "./IUserDialogInfo";
@ -175,6 +176,12 @@ export interface ISpt {
migrations?: Record<string, number>;
/** Cultist circle rewards received that are one time use, key (md5) is a combination of sacrificed + reward items */
cultistRewards?: Map<string, IAcceptedCultistReward>;
pendingPrestige?: IPendingPrestige;
}
export interface IPendingPrestige {
prestigeLevel: number;
items?: IObtainPrestigeRequest[];
}
export interface IAcceptedCultistReward {

View File

@ -1,15 +1,18 @@
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
import { ItemHelper } from "@spt/helpers/ItemHelper";
import { PrestigeHelper } from "@spt/helpers/PrestigeHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { QuestHelper } from "@spt/helpers/QuestHelper";
import { QuestRewardHelper } from "@spt/helpers/QuestRewardHelper";
import { RewardHelper } from "@spt/helpers/RewardHelper";
import { TraderHelper } from "@spt/helpers/TraderHelper";
import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { CustomisationSource, CustomisationType } from "@spt/models/eft/common/tables/ICustomisationStorage";
import { IItem } from "@spt/models/eft/common/tables/IItem";
import { ITemplateSide } from "@spt/models/eft/common/tables/IProfileTemplate";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { IProfileCreateRequestData } from "@spt/models/eft/profile/IProfileCreateRequestData";
import { IInraid, ISptProfile, IVitality } from "@spt/models/eft/profile/ISptProfile";
import { IInraid, IPendingPrestige, ISptProfile, IVitality } from "@spt/models/eft/profile/ISptProfile";
import { GameEditions } from "@spt/models/enums/GameEditions";
import { ItemTpl } from "@spt/models/enums/ItemTpl";
import { MessageType } from "@spt/models/enums/MessageType";
@ -41,16 +44,18 @@ export class CreateProfileService {
@inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("PrestigeHelper") protected prestigeHelper: PrestigeHelper,
@inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator,
@inject("QuestRewardHelper") protected questRewardHelper: QuestRewardHelper,
@inject("RewardHelper") protected rewardHelper: RewardHelper,
@inject("PrimaryCloner") protected cloner: ICloner,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
) {}
public async createProfile(sessionID: string, info: IProfileCreateRequestData): Promise<string> {
const account = this.saveServer.getProfile(sessionID).info;
const account = await this.cloner.cloneAsync(this.saveServer.getProfile(sessionID));
const profileTemplateClone: ITemplateSide = await this.cloner.cloneAsync(
this.databaseService.getProfiles()[account.edition][info.side.toLowerCase()],
this.databaseService.getProfiles()[account.info.edition][info.side.toLowerCase()],
);
const pmcData = profileTemplateClone.character;
@ -58,12 +63,12 @@ export class CreateProfileService {
this.deleteProfileBySessionId(sessionID);
// PMC
pmcData._id = account.id;
pmcData.aid = account.aid;
pmcData.savage = account.scavId;
pmcData._id = account.info.id;
pmcData.aid = account.info.aid;
pmcData.savage = account.info.scavId;
pmcData.sessionId = sessionID;
pmcData.Info.Nickname = info.nickname;
pmcData.Info.LowerNickname = account.username.toLowerCase();
pmcData.Info.LowerNickname = account.info.username.toLowerCase();
pmcData.Info.RegistrationDate = this.timeUtil.getTimestamp();
pmcData.Info.Voice = this.databaseService.getCustomization()[info.voiceId]._name;
pmcData.Stats = this.profileHelper.getDefaultCounters();
@ -77,8 +82,21 @@ export class CreateProfileService {
pmcData.CoopExtractCounts = {};
pmcData.Achievements = {};
if (typeof info.sptForcePrestigeLevel === "number") {
pmcData.Info.PrestigeLevel = info.sptForcePrestigeLevel;
// Process handling if the account has been forced to wipe
// BSG keeps both the achievements, prestige level and the total in-game time in a wipe.
if (account.characters.pmc.Achievements) {
pmcData.Achievements = account.characters.pmc.Achievements;
}
if (account.characters.pmc.Prestige) {
pmcData.Prestige = account.characters.pmc.Prestige;
pmcData.Info.PrestigeLevel = account.characters.pmc.Info.PrestigeLevel;
}
if (account.characters?.pmc?.Stats?.Eft) {
if (pmcData.Stats.Eft) {
pmcData.Stats.Eft.TotalInGameTime = account.characters.pmc.Stats.Eft.TotalInGameTime;
}
}
this.updateInventoryEquipmentId(pmcData);
@ -100,7 +118,7 @@ export class CreateProfileService {
// Create profile
const profileDetails: ISptProfile = {
info: account,
info: account.info,
characters: { pmc: pmcData, scav: {} as IPmcData },
suits: profileTemplateClone.suits,
userbuilds: profileTemplateClone.userbuilds,
@ -120,6 +138,54 @@ export class CreateProfileService {
this.saveServer.addProfile(profileDetails);
if (Object.keys(profileDetails.characters.pmc.Achievements).length > 0) {
const achievementsDb = this.databaseService.getTemplates().achievements;
const achievementRewardItemsToSend: IItem[] = [];
for (const achievementId in profileDetails.characters.pmc.Achievements) {
const rewards = achievementsDb.find((achievementDb) => achievementDb.id === achievementId)?.rewards;
if (!rewards) {
continue;
}
achievementRewardItemsToSend.push(
...this.rewardHelper.applyRewards(
rewards,
CustomisationSource.ACHIEVEMENT,
profileDetails,
profileDetails.characters.pmc,
achievementId,
),
);
}
if (achievementRewardItemsToSend.length > 0) {
this.mailSendService.sendLocalisedSystemMessageToPlayer(
profileDetails.info.id,
"670547bb5fa0b1a7c30d5836 0",
achievementRewardItemsToSend,
[],
31536000,
);
}
}
// Process handling if the account is forced to prestige, or if the account currently has any pending prestiges
if (info.sptForcePrestigeLevel || account.spt?.pendingPrestige) {
let pendingPrestige: IPendingPrestige;
if (account.spt.pendingPrestige) {
pendingPrestige = account.spt.pendingPrestige;
} else {
pendingPrestige = {
prestigeLevel: info.sptForcePrestigeLevel as number,
};
}
this.prestigeHelper.processPendingPrestige(account, profileDetails, pendingPrestige);
}
if (profileTemplateClone.trader.setQuestsAvailableForStart) {
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart]);
}