mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-13 04: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:
parent
96f5e0a6a2
commit
89fd1c95a7
@ -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 { 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 { 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 { IObtainPrestigeRequest } from "@spt/models/eft/prestige/IObtainPrestigeRequest";
|
||||||
import { IProfileCreateRequestData } from "@spt/models/eft/profile/IProfileCreateRequestData";
|
import { IPendingPrestige } from "@spt/models/eft/profile/ISptProfile";
|
||||||
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 { SaveServer } from "@spt/servers/SaveServer";
|
import { SaveServer } from "@spt/servers/SaveServer";
|
||||||
import { CreateProfileService } from "@spt/services/CreateProfileService";
|
|
||||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
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 { inject, injectable } from "tsyringe";
|
||||||
import type { IEmptyRequestData } from "../models/eft/common/IEmptyRequestData";
|
import type { IEmptyRequestData } from "../models/eft/common/IEmptyRequestData";
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class PrestigeController {
|
export class PrestigeController {
|
||||||
constructor(
|
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("SaveServer") protected saveServer: SaveServer,
|
||||||
@inject("DatabaseService") protected databaseService: DatabaseService,
|
@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,
|
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -65,14 +27,12 @@ export class PrestigeController {
|
|||||||
*/
|
*/
|
||||||
public async obtainPrestige(sessionId: string, request: IObtainPrestigeRequest[]): Promise<void> {
|
public async obtainPrestige(sessionId: string, request: IObtainPrestigeRequest[]): Promise<void> {
|
||||||
// Going to prestige 1
|
// Going to prestige 1
|
||||||
|
|
||||||
// transfer
|
// transfer
|
||||||
// 5% of skills should be transfered over
|
// 5% of skills should be transfered over
|
||||||
// 5% of mastering should be transfered over
|
// 5% of mastering should be transfered over
|
||||||
// earned achievements should be transfered over
|
// earned achievements should be transfered over
|
||||||
// profile stats should be transfered over
|
// profile stats should be transfered over
|
||||||
// prestige progress should be transfered over
|
// prestige progress should be transfered over
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
// trader standing
|
// trader standing
|
||||||
// task progress
|
// task progress
|
||||||
@ -80,142 +40,18 @@ export class PrestigeController {
|
|||||||
// stash
|
// stash
|
||||||
// hideout progress
|
// hideout progress
|
||||||
|
|
||||||
const prePrestigeProfileClone = this.cloner.clone(this.profileHelper.getFullProfile(sessionId));
|
const profile = 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reset profile
|
if (profile) {
|
||||||
await this.createProfileService.createProfile(sessionId, createRequest);
|
const pendingPrestige: IPendingPrestige = {
|
||||||
|
prestigeLevel: profile.characters.pmc.Info.PrestigeLevel + 1,
|
||||||
// Get freshly reset profile ready for editing
|
items: request,
|
||||||
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
|
profile.spt.pendingPrestige = pendingPrestige;
|
||||||
await this.saveServer.saveProfile(sessionId);
|
profile.info.wipe = true;
|
||||||
}
|
|
||||||
|
|
||||||
protected addPrestigeRewardsToProfile(sessionId: string, newProfile: ISptProfile, rewards: IReward[]) {
|
await this.saveServer.saveProfile(sessionId);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper";
|
|||||||
import { NotifierHelper } from "@spt/helpers/NotifierHelper";
|
import { NotifierHelper } from "@spt/helpers/NotifierHelper";
|
||||||
import { PaymentHelper } from "@spt/helpers/PaymentHelper";
|
import { PaymentHelper } from "@spt/helpers/PaymentHelper";
|
||||||
import { PresetHelper } from "@spt/helpers/PresetHelper";
|
import { PresetHelper } from "@spt/helpers/PresetHelper";
|
||||||
|
import { PrestigeHelper } from "@spt/helpers/PrestigeHelper";
|
||||||
import { ProbabilityHelper } from "@spt/helpers/ProbabilityHelper";
|
import { ProbabilityHelper } from "@spt/helpers/ProbabilityHelper";
|
||||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||||
import { QuestConditionHelper } from "@spt/helpers/QuestConditionHelper";
|
import { QuestConditionHelper } from "@spt/helpers/QuestConditionHelper";
|
||||||
@ -120,8 +121,8 @@ import { RagfairSellHelper } from "@spt/helpers/RagfairSellHelper";
|
|||||||
import { RagfairServerHelper } from "@spt/helpers/RagfairServerHelper";
|
import { RagfairServerHelper } from "@spt/helpers/RagfairServerHelper";
|
||||||
import { RagfairSortHelper } from "@spt/helpers/RagfairSortHelper";
|
import { RagfairSortHelper } from "@spt/helpers/RagfairSortHelper";
|
||||||
import { RepairHelper } from "@spt/helpers/RepairHelper";
|
import { RepairHelper } from "@spt/helpers/RepairHelper";
|
||||||
import { RewardHelper } from "@spt/helpers/RewardHelper";
|
|
||||||
import { RepeatableQuestHelper } from "@spt/helpers/RepeatableQuestHelper";
|
import { RepeatableQuestHelper } from "@spt/helpers/RepeatableQuestHelper";
|
||||||
|
import { RewardHelper } from "@spt/helpers/RewardHelper";
|
||||||
import { SecureContainerHelper } from "@spt/helpers/SecureContainerHelper";
|
import { SecureContainerHelper } from "@spt/helpers/SecureContainerHelper";
|
||||||
import { TradeHelper } from "@spt/helpers/TradeHelper";
|
import { TradeHelper } from "@spt/helpers/TradeHelper";
|
||||||
import { TraderAssortHelper } from "@spt/helpers/TraderAssortHelper";
|
import { TraderAssortHelper } from "@spt/helpers/TraderAssortHelper";
|
||||||
@ -636,6 +637,7 @@ export class Container {
|
|||||||
});
|
});
|
||||||
depContainer.register<BotDifficultyHelper>("BotDifficultyHelper", { useClass: BotDifficultyHelper });
|
depContainer.register<BotDifficultyHelper>("BotDifficultyHelper", { useClass: BotDifficultyHelper });
|
||||||
depContainer.register<RepeatableQuestHelper>("RepeatableQuestHelper", { useClass: RepeatableQuestHelper });
|
depContainer.register<RepeatableQuestHelper>("RepeatableQuestHelper", { useClass: RepeatableQuestHelper });
|
||||||
|
depContainer.register<PrestigeHelper>("PrestigeHelper", PrestigeHelper);
|
||||||
|
|
||||||
// ChatBots
|
// ChatBots
|
||||||
depContainer.register<SptDialogueChatBot>("SptDialogueChatBot", SptDialogueChatBot);
|
depContainer.register<SptDialogueChatBot>("SptDialogueChatBot", SptDialogueChatBot);
|
||||||
|
139
project/src/helpers/PrestigeHelper.ts
Normal file
139
project/src/helpers/PrestigeHelper.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -198,6 +198,7 @@ export class ProfileHelper {
|
|||||||
cultistRewards: new Map(),
|
cultistRewards: new Map(),
|
||||||
mods: [],
|
mods: [],
|
||||||
receivedGifts: [],
|
receivedGifts: [],
|
||||||
|
pendingPrestige: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import { IReward } from "@spt/models/eft/common/tables/IReward";
|
|||||||
import { IHideoutProduction } from "@spt/models/eft/hideout/IHideoutProduction";
|
import { IHideoutProduction } from "@spt/models/eft/hideout/IHideoutProduction";
|
||||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||||
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
|
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
|
||||||
import { BaseClasses } from "@spt/models/enums/BaseClasses";
|
|
||||||
import { RewardType } from "@spt/models/enums/RewardType";
|
import { RewardType } from "@spt/models/enums/RewardType";
|
||||||
import { SkillTypes } from "@spt/models/enums/SkillTypes";
|
import { SkillTypes } from "@spt/models/enums/SkillTypes";
|
||||||
import type { ILogger } from "@spt/models/spt/utils/ILogger";
|
import type { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||||
@ -348,8 +347,10 @@ export class RewardHelper {
|
|||||||
* @param achievementId Id of achievement to add
|
* @param achievementId Id of achievement to add
|
||||||
*/
|
*/
|
||||||
public addAchievementToProfile(fullProfile: ISptProfile, achievementId: string): void {
|
public addAchievementToProfile(fullProfile: ISptProfile, achievementId: string): void {
|
||||||
// Add achievement id to profile with timestamp it was unlocked
|
if (!fullProfile.characters.pmc.Achievements[achievementId]) {
|
||||||
fullProfile.characters.pmc.Achievements[achievementId] = this.timeUtil.getTimestamp();
|
// Add achievement id to profile with timestamp it was unlocked
|
||||||
|
fullProfile.characters.pmc.Achievements[achievementId] = this.timeUtil.getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
// Check for any customisation unlocks
|
// Check for any customisation unlocks
|
||||||
const achievementDataDb = this.databaseService
|
const achievementDataDb = this.databaseService
|
||||||
|
@ -5,6 +5,7 @@ import { EquipmentBuildType } from "@spt/models/enums/EquipmentBuildType";
|
|||||||
import { MemberCategory } from "@spt/models/enums/MemberCategory";
|
import { MemberCategory } from "@spt/models/enums/MemberCategory";
|
||||||
import { MessageType } from "@spt/models/enums/MessageType";
|
import { MessageType } from "@spt/models/enums/MessageType";
|
||||||
import { IProfileChangeEvent } from "@spt/models/spt/dialog/ISendMessageDetails";
|
import { IProfileChangeEvent } from "@spt/models/spt/dialog/ISendMessageDetails";
|
||||||
|
import { IObtainPrestigeRequest } from "../prestige/IObtainPrestigeRequest";
|
||||||
import { ISystemData } from "./ISystemData";
|
import { ISystemData } from "./ISystemData";
|
||||||
import { IUserDialogInfo } from "./IUserDialogInfo";
|
import { IUserDialogInfo } from "./IUserDialogInfo";
|
||||||
|
|
||||||
@ -175,6 +176,12 @@ export interface ISpt {
|
|||||||
migrations?: Record<string, number>;
|
migrations?: Record<string, number>;
|
||||||
/** Cultist circle rewards received that are one time use, key (md5) is a combination of sacrificed + reward items */
|
/** Cultist circle rewards received that are one time use, key (md5) is a combination of sacrificed + reward items */
|
||||||
cultistRewards?: Map<string, IAcceptedCultistReward>;
|
cultistRewards?: Map<string, IAcceptedCultistReward>;
|
||||||
|
pendingPrestige?: IPendingPrestige;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPendingPrestige {
|
||||||
|
prestigeLevel: number;
|
||||||
|
items?: IObtainPrestigeRequest[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAcceptedCultistReward {
|
export interface IAcceptedCultistReward {
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
|
import { PlayerScavGenerator } from "@spt/generators/PlayerScavGenerator";
|
||||||
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
||||||
|
import { PrestigeHelper } from "@spt/helpers/PrestigeHelper";
|
||||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||||
import { QuestHelper } from "@spt/helpers/QuestHelper";
|
import { QuestHelper } from "@spt/helpers/QuestHelper";
|
||||||
import { QuestRewardHelper } from "@spt/helpers/QuestRewardHelper";
|
import { QuestRewardHelper } from "@spt/helpers/QuestRewardHelper";
|
||||||
|
import { RewardHelper } from "@spt/helpers/RewardHelper";
|
||||||
import { TraderHelper } from "@spt/helpers/TraderHelper";
|
import { TraderHelper } from "@spt/helpers/TraderHelper";
|
||||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||||
import { CustomisationSource, CustomisationType } from "@spt/models/eft/common/tables/ICustomisationStorage";
|
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 { ITemplateSide } from "@spt/models/eft/common/tables/IProfileTemplate";
|
||||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||||
import { IProfileCreateRequestData } from "@spt/models/eft/profile/IProfileCreateRequestData";
|
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 { GameEditions } from "@spt/models/enums/GameEditions";
|
||||||
import { ItemTpl } from "@spt/models/enums/ItemTpl";
|
import { ItemTpl } from "@spt/models/enums/ItemTpl";
|
||||||
import { MessageType } from "@spt/models/enums/MessageType";
|
import { MessageType } from "@spt/models/enums/MessageType";
|
||||||
@ -41,16 +44,18 @@ export class CreateProfileService {
|
|||||||
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
@inject("TraderHelper") protected traderHelper: TraderHelper,
|
||||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||||
|
@inject("PrestigeHelper") protected prestigeHelper: PrestigeHelper,
|
||||||
@inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator,
|
@inject("PlayerScavGenerator") protected playerScavGenerator: PlayerScavGenerator,
|
||||||
@inject("QuestRewardHelper") protected questRewardHelper: QuestRewardHelper,
|
@inject("QuestRewardHelper") protected questRewardHelper: QuestRewardHelper,
|
||||||
|
@inject("RewardHelper") protected rewardHelper: RewardHelper,
|
||||||
@inject("PrimaryCloner") protected cloner: ICloner,
|
@inject("PrimaryCloner") protected cloner: ICloner,
|
||||||
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async createProfile(sessionID: string, info: IProfileCreateRequestData): Promise<string> {
|
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(
|
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;
|
const pmcData = profileTemplateClone.character;
|
||||||
|
|
||||||
@ -58,12 +63,12 @@ export class CreateProfileService {
|
|||||||
this.deleteProfileBySessionId(sessionID);
|
this.deleteProfileBySessionId(sessionID);
|
||||||
|
|
||||||
// PMC
|
// PMC
|
||||||
pmcData._id = account.id;
|
pmcData._id = account.info.id;
|
||||||
pmcData.aid = account.aid;
|
pmcData.aid = account.info.aid;
|
||||||
pmcData.savage = account.scavId;
|
pmcData.savage = account.info.scavId;
|
||||||
pmcData.sessionId = sessionID;
|
pmcData.sessionId = sessionID;
|
||||||
pmcData.Info.Nickname = info.nickname;
|
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.RegistrationDate = this.timeUtil.getTimestamp();
|
||||||
pmcData.Info.Voice = this.databaseService.getCustomization()[info.voiceId]._name;
|
pmcData.Info.Voice = this.databaseService.getCustomization()[info.voiceId]._name;
|
||||||
pmcData.Stats = this.profileHelper.getDefaultCounters();
|
pmcData.Stats = this.profileHelper.getDefaultCounters();
|
||||||
@ -77,8 +82,21 @@ export class CreateProfileService {
|
|||||||
pmcData.CoopExtractCounts = {};
|
pmcData.CoopExtractCounts = {};
|
||||||
pmcData.Achievements = {};
|
pmcData.Achievements = {};
|
||||||
|
|
||||||
if (typeof info.sptForcePrestigeLevel === "number") {
|
// Process handling if the account has been forced to wipe
|
||||||
pmcData.Info.PrestigeLevel = info.sptForcePrestigeLevel;
|
// 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);
|
this.updateInventoryEquipmentId(pmcData);
|
||||||
@ -100,7 +118,7 @@ export class CreateProfileService {
|
|||||||
|
|
||||||
// Create profile
|
// Create profile
|
||||||
const profileDetails: ISptProfile = {
|
const profileDetails: ISptProfile = {
|
||||||
info: account,
|
info: account.info,
|
||||||
characters: { pmc: pmcData, scav: {} as IPmcData },
|
characters: { pmc: pmcData, scav: {} as IPmcData },
|
||||||
suits: profileTemplateClone.suits,
|
suits: profileTemplateClone.suits,
|
||||||
userbuilds: profileTemplateClone.userbuilds,
|
userbuilds: profileTemplateClone.userbuilds,
|
||||||
@ -120,6 +138,54 @@ export class CreateProfileService {
|
|||||||
|
|
||||||
this.saveServer.addProfile(profileDetails);
|
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) {
|
if (profileTemplateClone.trader.setQuestsAvailableForStart) {
|
||||||
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart]);
|
this.questHelper.addAllQuestsToProfile(profileDetails.characters.pmc, [QuestStatus.AvailableForStart]);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user