mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-13 09:50:43 -05:00
Refactoring of RepeatableQuestController
This commit is contained in:
parent
5cae90c726
commit
d9f8ba653a
@ -89,6 +89,6 @@ export class QuestCallbacks
|
|||||||
sessionID: string,
|
sessionID: string,
|
||||||
): IGetBodyResponseData<IPmcDataRepeatableQuest[]>
|
): IGetBodyResponseData<IPmcDataRepeatableQuest[]>
|
||||||
{
|
{
|
||||||
return this.httpResponse.getBody(this.repeatableQuestController.getClientRepeatableQuests(info, sessionID));
|
return this.httpResponse.getBody(this.repeatableQuestController.getClientRepeatableQuests(sessionID));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { RepeatableQuestGenerator } from "@spt/generators/RepeatableQuestGenerat
|
|||||||
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 { RepeatableQuestHelper } from "@spt/helpers/RepeatableQuestHelper";
|
import { RepeatableQuestHelper } from "@spt/helpers/RepeatableQuestHelper";
|
||||||
import { IEmptyRequestData } from "@spt/models/eft/common/IEmptyRequestData";
|
|
||||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||||
import {
|
import {
|
||||||
IChangeRequirement,
|
IChangeRequirement,
|
||||||
@ -80,12 +79,11 @@ export class RepeatableQuestController
|
|||||||
* (if the are on "Succeed" but not "Completed" we keep them, to allow the player to complete them and get the rewards)
|
* (if the are on "Succeed" but not "Completed" we keep them, to allow the player to complete them and get the rewards)
|
||||||
* The new quests generated are again persisted in profile.RepeatableQuests
|
* The new quests generated are again persisted in profile.RepeatableQuests
|
||||||
*
|
*
|
||||||
* @param {string} _info Request from client
|
|
||||||
* @param {string} sessionID Player's session id
|
* @param {string} sessionID Player's session id
|
||||||
*
|
*
|
||||||
* @returns {array} Array of "repeatableQuestObjects" as described above
|
* @returns {array} Array of "repeatableQuestObjects" as described above
|
||||||
*/
|
*/
|
||||||
public getClientRepeatableQuests(_info: IEmptyRequestData, sessionID: string): IPmcDataRepeatableQuest[]
|
public getClientRepeatableQuests(sessionID: string): IPmcDataRepeatableQuest[]
|
||||||
{
|
{
|
||||||
const returnData: Array<IPmcDataRepeatableQuest> = [];
|
const returnData: Array<IPmcDataRepeatableQuest> = [];
|
||||||
const fullProfile = this.profileHelper.getFullProfile(sessionID)!;
|
const fullProfile = this.profileHelper.getFullProfile(sessionID)!;
|
||||||
@ -99,14 +97,8 @@ export class RepeatableQuestController
|
|||||||
const currentRepeatableQuestType = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
|
const currentRepeatableQuestType = this.getRepeatableQuestSubTypeFromProfile(repeatableConfig, pmcData);
|
||||||
const repeatableType = repeatableConfig.name.toLowerCase();
|
const repeatableType = repeatableConfig.name.toLowerCase();
|
||||||
|
|
||||||
// Is PMC and can't see dailies yet, skip
|
const canAccessRepeatables = this.canProfileAccessRepeatableQuests(repeatableConfig, pmcData);
|
||||||
if ((repeatableConfig.side === "Pmc" && !this.playerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig)))
|
if (!canAccessRepeatables)
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is Scav and can't see dailies yet, skip
|
|
||||||
if ((repeatableConfig.side === "Scav" && !this.playerHasDailyScavQuestsUnlocked(pmcData)))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -222,6 +214,29 @@ export class RepeatableQuestController
|
|||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a repeatable quest type (daily/weekly) is active for the given profile
|
||||||
|
* @param repeatableConfig Repeatable quest config
|
||||||
|
* @param pmcData Player profile
|
||||||
|
* @returns True if profile is allowed to access dailies
|
||||||
|
*/
|
||||||
|
protected canProfileAccessRepeatableQuests(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): boolean
|
||||||
|
{
|
||||||
|
// PMC and daily quests not unlocked yet
|
||||||
|
if (repeatableConfig.side === "Pmc" && !this.playerHasDailyPmcQuestsUnlocked(pmcData, repeatableConfig))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scav and daily quests not unlocked yet
|
||||||
|
if (repeatableConfig.side === "Scav" && !this.playerHasDailyScavQuestsUnlocked(pmcData))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does player have daily scav quests unlocked
|
* Does player have daily scav quests unlocked
|
||||||
* @param pmcData Player profile to check
|
* @param pmcData Player profile to check
|
||||||
@ -346,7 +361,10 @@ export class RepeatableQuestController
|
|||||||
{
|
{
|
||||||
const questPool = this.createBaseQuestPool(repeatableConfig);
|
const questPool = this.createBaseQuestPool(repeatableConfig);
|
||||||
|
|
||||||
const locations = this.getAllowedLocations(repeatableConfig.locations, pmcLevel);
|
// Get the allowed locations based on the PMC's level
|
||||||
|
const locations = this.getAllowedLocationsForPmcLevel(repeatableConfig.locations, pmcLevel);
|
||||||
|
|
||||||
|
// Populate Exploration and Pickup quest locations
|
||||||
for (const location in locations)
|
for (const location in locations)
|
||||||
{
|
{
|
||||||
if (location !== ELocationName.ANY)
|
if (location !== ELocationName.ANY)
|
||||||
@ -361,22 +379,25 @@ export class RepeatableQuestController
|
|||||||
|
|
||||||
const eliminationConfig = this.repeatableQuestHelper.getEliminationConfigByPmcLevel(pmcLevel, repeatableConfig);
|
const eliminationConfig = this.repeatableQuestHelper.getEliminationConfigByPmcLevel(pmcLevel, repeatableConfig);
|
||||||
const targetsConfig = this.repeatableQuestHelper.probabilityObjectArray(eliminationConfig.targets);
|
const targetsConfig = this.repeatableQuestHelper.probabilityObjectArray(eliminationConfig.targets);
|
||||||
for (const probabilityObject of targetsConfig)
|
|
||||||
|
// Populate Elimination quest targets and their locations
|
||||||
|
for (const { data: target, key: targetKey } of targetsConfig)
|
||||||
{
|
{
|
||||||
// Target is boss
|
// Target is boss
|
||||||
if (probabilityObject.data.isBoss)
|
if (target.isBoss)
|
||||||
{
|
{
|
||||||
questPool.pool.Elimination.targets[probabilityObject.key] = { locations: ["any"] };
|
questPool.pool.Elimination.targets[targetKey] = { locations: ["any"] };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Non-boss targets
|
||||||
const possibleLocations = Object.keys(locations);
|
const possibleLocations = Object.keys(locations);
|
||||||
|
|
||||||
// Set possible locations for elimination task, if target is savage, exclude labs from locations
|
const allowedLocations = (targetKey === "Savage")
|
||||||
questPool.pool.Elimination.targets[probabilityObject.key]
|
? possibleLocations.filter((location) => location !== "laboratory") // Exclude labs for Savage targets.
|
||||||
= probabilityObject.key === "Savage"
|
: possibleLocations;
|
||||||
? { locations: possibleLocations.filter((x) => x !== "laboratory") }
|
|
||||||
: { locations: possibleLocations };
|
questPool.pool.Elimination.targets[targetKey] = { locations: allowedLocations };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,10 +415,10 @@ export class RepeatableQuestController
|
|||||||
/**
|
/**
|
||||||
* Return the locations this PMC is allowed to get daily quests for based on their level
|
* Return the locations this PMC is allowed to get daily quests for based on their level
|
||||||
* @param locations The original list of locations
|
* @param locations The original list of locations
|
||||||
* @param pmcLevel The level of the player PMC
|
* @param pmcLevel The players level
|
||||||
* @returns A filtered list of locations that allow the player PMC level to access it
|
* @returns A filtered list of locations that allow the player PMC level to access it
|
||||||
*/
|
*/
|
||||||
protected getAllowedLocations(
|
protected getAllowedLocationsForPmcLevel(
|
||||||
locations: Record<ELocationName, string[]>,
|
locations: Record<ELocationName, string[]>,
|
||||||
pmcLevel: number,
|
pmcLevel: number,
|
||||||
): Partial<Record<ELocationName, string[]>>
|
): Partial<Record<ELocationName, string[]>>
|
||||||
@ -432,12 +453,13 @@ export class RepeatableQuestController
|
|||||||
*/
|
*/
|
||||||
protected isPmcLevelAllowedOnLocation(location: string, pmcLevel: number): boolean
|
protected isPmcLevelAllowedOnLocation(location: string, pmcLevel: number): boolean
|
||||||
{
|
{
|
||||||
|
// All PMC levels are allowed for 'any' location requirement
|
||||||
if (location === ELocationName.ANY)
|
if (location === ELocationName.ANY)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const locationBase = this.databaseService.getLocation(location.toLowerCase()).base;
|
const locationBase = this.databaseService.getLocation(location.toLowerCase())?.base;
|
||||||
if (!locationBase)
|
if (!locationBase)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -469,6 +491,12 @@ export class RepeatableQuestController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle RepeatableQuestChange event
|
* Handle RepeatableQuestChange event
|
||||||
|
*
|
||||||
|
* Replace a players repeatable quest
|
||||||
|
* @param pmcData Player profile
|
||||||
|
* @param changeRequest Request object
|
||||||
|
* @param sessionID Session id
|
||||||
|
* @returns IItemEventRouterResponse
|
||||||
*/
|
*/
|
||||||
public changeRepeatableQuest(
|
public changeRepeatableQuest(
|
||||||
pmcData: IPmcData,
|
pmcData: IPmcData,
|
||||||
@ -476,9 +504,12 @@ export class RepeatableQuestController
|
|||||||
sessionID: string,
|
sessionID: string,
|
||||||
): IItemEventRouterResponse
|
): IItemEventRouterResponse
|
||||||
{
|
{
|
||||||
|
const output = this.eventOutputHolder.getOutput(sessionID);
|
||||||
|
|
||||||
|
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
||||||
|
|
||||||
let repeatableToChange: IPmcDataRepeatableQuest;
|
let repeatableToChange: IPmcDataRepeatableQuest;
|
||||||
let changeRequirement: IChangeRequirement;
|
let changeRequirement: IChangeRequirement;
|
||||||
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
|
||||||
|
|
||||||
// The trader existing quest is linked to
|
// The trader existing quest is linked to
|
||||||
let replacedQuestTraderId: string;
|
let replacedQuestTraderId: string;
|
||||||
@ -521,8 +552,6 @@ export class RepeatableQuestController
|
|||||||
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]),
|
changeStandingCost: this.randomUtil.getArrayValue([0, 0.01]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const fullProfile = this.profileHelper.getFullProfile(sessionID);
|
|
||||||
|
|
||||||
// Find quest we're replacing in pmc profile quests array and remove it
|
// Find quest we're replacing in pmc profile quests array and remove it
|
||||||
this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, pmcData.Quests);
|
this.questHelper.findAndRemoveQuestFromArrayIfExists(questToReplace._id, pmcData.Quests);
|
||||||
|
|
||||||
@ -544,7 +573,6 @@ export class RepeatableQuestController
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = this.eventOutputHolder.getOutput(sessionID);
|
|
||||||
if (!repeatableToChange)
|
if (!repeatableToChange)
|
||||||
{
|
{
|
||||||
// Unable to find quest being replaced
|
// Unable to find quest being replaced
|
||||||
@ -584,9 +612,11 @@ export class RepeatableQuestController
|
|||||||
repeatableConfig: IRepeatableQuestConfig,
|
repeatableConfig: IRepeatableQuestConfig,
|
||||||
): IRepeatableQuest
|
): IRepeatableQuest
|
||||||
{
|
{
|
||||||
|
const maxAttempts = 10;
|
||||||
let newRepeatableQuest: IRepeatableQuest = undefined;
|
let newRepeatableQuest: IRepeatableQuest = undefined;
|
||||||
let attemptsToGenerateQuest = 0;
|
let attempts = 0;
|
||||||
while (!newRepeatableQuest && questTypePool.types.length > 0)
|
while (attempts < maxAttempts
|
||||||
|
&& questTypePool.types.length > 0)
|
||||||
{
|
{
|
||||||
newRepeatableQuest = this.repeatableQuestGenerator.generateRepeatableQuest(
|
newRepeatableQuest = this.repeatableQuestGenerator.generateRepeatableQuest(
|
||||||
pmcData.Info.Level,
|
pmcData.Info.Level,
|
||||||
@ -594,14 +624,21 @@ export class RepeatableQuestController
|
|||||||
questTypePool,
|
questTypePool,
|
||||||
repeatableConfig,
|
repeatableConfig,
|
||||||
);
|
);
|
||||||
attemptsToGenerateQuest++;
|
|
||||||
if (attemptsToGenerateQuest > 10)
|
if (newRepeatableQuest)
|
||||||
{
|
{
|
||||||
this.logger.debug(
|
// Successfully generated a quest, exit loop
|
||||||
"We were stuck in repeatable quest generation. This should never happen. Please report",
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempts > maxAttempts)
|
||||||
|
{
|
||||||
|
this.logger.debug(
|
||||||
|
"We were stuck in repeatable quest generation. This should never happen. Please report",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newRepeatableQuest;
|
return newRepeatableQuest;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user