0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-12 16:10:43 -05:00

Add quest production unlocks to the PMC Profile fixer service

This commit is contained in:
DrakiaXYZ 2024-11-30 10:08:51 -08:00
parent 063d1eaea8
commit 8b1a3158cb
2 changed files with 102 additions and 15 deletions

View File

@ -10,6 +10,7 @@ import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { Common, IQuestStatus } from "@spt/models/eft/common/tables/IBotBase";
import { IItem } from "@spt/models/eft/common/tables/IItem";
import { IQuest, IQuestCondition, IQuestReward } from "@spt/models/eft/common/tables/IQuest";
import { IHideoutProduction } from "@spt/models/eft/hideout/IHideoutProduction";
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
import { IAcceptQuestRequestData } from "@spt/models/eft/quests/IAcceptQuestRequestData";
import { ICompleteQuestRequestData } from "@spt/models/eft/quests/ICompleteQuestRequestData";
@ -1034,6 +1035,34 @@ export class QuestHelper {
sessionID: string,
response: IItemEventRouterResponse,
): void {
const matchingProductions = this.getRewardProductionMatch(craftUnlockReward, questDetails);
if (matchingProductions.length !== 1) {
this.logger.error(
this.localisationService.getText("quest-unable_to_find_matching_hideout_production", {
questName: questDetails.QuestName,
matchCount: matchingProductions.length,
}),
);
return;
}
// Add above match to pmc profile + client response
const matchingCraftId = matchingProductions[0]._id;
pmcData.UnlockedInfo.unlockedProductionRecipe.push(matchingCraftId);
response.profileChanges[sessionID].recipeUnlocked[matchingCraftId] = true;
}
/**
* Find hideout craft id for the specified quest reward
* @param craftUnlockReward
* @param questDetails
* @returns
*/
public getRewardProductionMatch(
craftUnlockReward: IQuestReward,
questDetails: IQuest,
): IHideoutProduction[] {
// Get hideout crafts and find those that match by areatype/required level/end product tpl - hope for just one match
const craftingRecipes = this.databaseService.getHideout().production.recipes;
@ -1055,23 +1084,9 @@ export class QuestHelper {
matchingProductions = matchingProductions.filter((prod) =>
prod.requirements.some((requirement) => requirement.questId === questDetails._id),
);
if (matchingProductions.length !== 1) {
this.logger.error(
this.localisationService.getText("quest-unable_to_find_matching_hideout_production", {
questName: questDetails.QuestName,
matchCount: matchingProductions.length,
}),
);
return;
}
}
// Add above match to pmc profile + client response
const matchingCraftId = matchingProductions[0]._id;
pmcData.UnlockedInfo.unlockedProductionRecipe.push(matchingCraftId);
response.profileChanges[sessionID].recipeUnlocked[matchingCraftId] = true;
return matchingProductions;
}
/**

View File

@ -2,9 +2,11 @@ import { HideoutHelper } from "@spt/helpers/HideoutHelper";
import { InventoryHelper } from "@spt/helpers/InventoryHelper";
import { ItemHelper } from "@spt/helpers/ItemHelper";
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { QuestHelper } from "@spt/helpers/QuestHelper";
import { TraderHelper } from "@spt/helpers/TraderHelper";
import { IPmcData } from "@spt/models/eft/common/IPmcData";
import { IBonus, IHideoutSlot } from "@spt/models/eft/common/tables/IBotBase";
import { IQuest, IQuestReward } from "@spt/models/eft/common/tables/IQuest";
import { IPmcDataRepeatableQuest, IRepeatableQuest } from "@spt/models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem";
import { IStageBonus } from "@spt/models/eft/hideout/IHideoutArea";
@ -12,6 +14,8 @@ import { IEquipmentBuild, IMagazineBuild, ISptProfile, IWeaponBuild } from "@spt
import { BonusType } from "@spt/models/enums/BonusType";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { HideoutAreas } from "@spt/models/enums/HideoutAreas";
import { QuestRewardType } from "@spt/models/enums/QuestRewardType";
import { QuestStatus } from "@spt/models/enums/QuestStatus";
import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig";
import { IRagfairConfig } from "@spt/models/spt/config/IRagfairConfig";
import { ILogger } from "@spt/models/spt/utils/ILogger";
@ -45,6 +49,7 @@ export class ProfileFixerService {
@inject("HashUtil") protected hashUtil: HashUtil,
@inject("ConfigServer") protected configServer: ConfigServer,
@inject("PrimaryCloner") protected cloner: ICloner,
@inject("QuestHelper") protected questHelper: QuestHelper,
) {
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
@ -58,6 +63,7 @@ export class ProfileFixerService {
this.removeDanglingConditionCounters(pmcProfile);
this.removeDanglingTaskConditionCounters(pmcProfile);
this.removeOrphanedQuests(pmcProfile);
this.verifyQuestProductionUnlocks(pmcProfile);
if (pmcProfile.Hideout) {
this.addHideoutEliteSlots(pmcProfile);
@ -264,6 +270,72 @@ export class ProfileFixerService {
}
}
/**
* Verify that all quest production unlocks have been applied to the PMC Profile
* @param pmcProfile The profile to validate quest productions for
*/
protected verifyQuestProductionUnlocks(pmcProfile: IPmcData): void {
const start = performance.now();
const quests = this.databaseService.getQuests();
const profileQuests = pmcProfile.Quests;
for (const profileQuest of profileQuests)
{
const quest = quests[profileQuest.qid];
// For started or successful quests, check for unlocks in the `Started` rewards
if (profileQuest.status == QuestStatus.Started || profileQuest.status == QuestStatus.Success)
{
const productionRewards = quest.rewards.Started?.filter(reward => reward.type == QuestRewardType.PRODUCTIONS_SCHEME);
productionRewards?.forEach(reward => this.verifyQuestProductionUnlock(pmcProfile, reward, quest));
}
// For successful quests, check for unlocks in the `Success` rewards
if (profileQuest.status == QuestStatus.Success)
{
const productionRewards = quest.rewards.Success?.filter(reward => reward.type == QuestRewardType.PRODUCTIONS_SCHEME);
productionRewards?.forEach(reward => this.verifyQuestProductionUnlock(pmcProfile, reward, quest));
}
}
const validateTime = performance.now() - start
this.logger.debug(`Quest Production Unlock validation took: ${validateTime.toFixed(2)}ms`);
}
/**
* Validate that the given profile has the given quest reward production scheme unlocked, and add it if not
* @param pmcProfile Profile to check
* @param productionUnlockReward The quest reward to validate
* @param questDetails The quest the reward belongs to
* @returns
*/
protected verifyQuestProductionUnlock(
pmcProfile: IPmcData,
productionUnlockReward: IQuestReward,
questDetails: IQuest
): void {
const matchingProductions = this.questHelper.getRewardProductionMatch(productionUnlockReward, questDetails);
if (matchingProductions.length !== 1) {
this.logger.error(
this.localisationService.getText("quest-unable_to_find_matching_hideout_production", {
questName: questDetails.QuestName,
matchCount: matchingProductions.length,
}),
);
return;
}
// Add above match to pmc profile
const matchingProductionId = matchingProductions[0]._id;
if (!pmcProfile.UnlockedInfo.unlockedProductionRecipe.includes(matchingProductionId))
{
pmcProfile.UnlockedInfo.unlockedProductionRecipe.push(matchingProductionId);
this.logger.debug(`Added production ${matchingProductionId} to unlocked production recipes for ${questDetails.QuestName}`);
}
}
/**
* If the profile has elite Hideout Managment skill, add the additional slots from globals
* NOTE: This seems redundant, but we will leave it here just incase.