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

Implement Pickup quests for scav

It seems like these are scav-only
This commit is contained in:
Dev 2023-10-17 16:28:48 +01:00
parent e10b62a5b1
commit 650a3173c8
7 changed files with 235 additions and 12 deletions

View File

@ -11,7 +11,8 @@
"scav": { "scav": {
"elimination": "62825ef60e88d037dc1eb428", "elimination": "62825ef60e88d037dc1eb428",
"completion": "628f588ebb558574b2260fe5", "completion": "628f588ebb558574b2260fe5",
"exploration": "62825ef60e88d037dc1eb42c" "exploration": "62825ef60e88d037dc1eb42c",
"pickup": "628f588ebb558574b2260fe5"
} }
}, },
"showNonSeasonalEventQuests": false, "showNonSeasonalEventQuests": false,
@ -1297,8 +1298,10 @@
"name": "Daily_Savage", "name": "Daily_Savage",
"side": "Scav", "side": "Scav",
"types": [ "types": [
"Exploration",
"Elimination", "Elimination",
"Completion" "Completion",
"Pickup"
], ],
"resetTime": 86400, "resetTime": 86400,
"numQuests": 1, "numQuests": 1,
@ -1324,7 +1327,7 @@
}, },
"traderWhitelist": [{ "traderWhitelist": [{
"traderId": "579dc571d53a0658a154fbec", "traderId": "579dc571d53a0658a154fbec",
"questTypes": ["Completion", "Exploration", "Elimination"] "questTypes": ["Completion", "Exploration", "Elimination", "Pickup"]
} }
], ],
"questConfig": { "questConfig": {
@ -1341,6 +1344,23 @@
] ]
} }
}, },
"Pickup": {
"ItemTypeToFetchWithMaxCount": [
{"itemType": "5b47574386f77428ca22b335", "minPickupCount": 2, "maxPickupCount": 4},
{"itemType": "5b47574386f77428ca22b336", "minPickupCount": 2, "maxPickupCount": 4},
{"itemType": "5b47574386f77428ca22b2ee", "minPickupCount": 2, "maxPickupCount": 3},
{"itemType": "5b47574386f77428ca22b2ef", "minPickupCount": 1, "maxPickupCount": 2},
{"itemType": "5b47574386f77428ca22b33a", "minPickupCount": 1, "maxPickupCount": 2},
{"itemType": "5b5f701386f774093f2ecf0f", "minPickupCount": 1, "maxPickupCount": 2},
{"itemType": "5b5f754a86f774094242f19b", "minPickupCount": 1, "maxPickupCount": 3},
{"itemType": "5b5f75c686f774094242f19f", "minPickupCount": 1, "maxPickupCount": 2},
{"itemType": "5b5f792486f77447ed5636b3", "minPickupCount": 1, "maxPickupCount": 2},
{"itemType": "5b5f73ab86f774094242f195", "minPickupCount": 1, "maxPickupCount": 2},
{}
],
"ItemTypesToFetch": ["5b47574386f77428ca22b335", "5b47574386f77428ca22b336", "5b47574386f77428ca22b2ee"],
"maxItemFetchCount": 3
},
"Completion": { "Completion": {
"minRequestedAmount": 1, "minRequestedAmount": 1,
"maxRequestedAmount": 5, "maxRequestedAmount": 5,

View File

@ -182,7 +182,111 @@
} }
], ],
"changeStandingCost": 0 "changeStandingCost": 0
} },
"Pickup": {
"_id": "64cfb3818db9f48b3f0b0a759",
"traderId": "579dc571d53a0658a154fbec",
"location": "any",
"image": "/files/quest/icon/62bd61b1b818ff064405b827.jpg",
"type": "PickUp",
"isKey": false,
"restartable": false,
"instantComplete": false,
"secretQuest": false,
"canShowNotificationsInGame": true,
"rewards": {
"Started": [],
"Success": [],
"Fail": []
},
"conditions": {
"AvailableForStart": [],
"AvailableForFinish": [{
"_props": {
"id": "64cfb3818db9f48b3f0b0a6f",
"parentId": "",
"dynamicLocale": false,
"index": 0,
"visibilityConditions": [],
"globalQuestCounterId": null,
"target": ["5b47574386f77428ca22b336"],
"value": 7,
"minDurability": 0,
"maxDurability": 100,
"dogtagLevel": 0,
"onlyFoundInRaid": false,
"isEncoded": false,
"countInRaid": true
},
"_parent": "FindItem",
"dynamicLocale": false
}, {
"_props": {
"id": "64cfb3818db9f48b3f0b0a74",
"parentId": "",
"dynamicLocale": true,
"index": 0,
"visibilityConditions": [],
"globalQuestCounterId": null,
"value": 1,
"type": "PickUp",
"completeInSeconds": 0,
"oneSessionOnly": true,
"doNotResetIfCounterCompleted": false,
"counter": {
"id": "64cfb3818db9f48b3f0b0a73",
"conditions": [{
"_props": {
"target": ["any"],
"id": "64cfb3818db9f48b3f0b0a70",
"dynamicLocale": true
},
"_parent": "Location"
}, {
"_props": {
"status": ["Survived"],
"id": "64cfb3818db9f48b3f0b0a71",
"dynamicLocale": true
},
"_parent": "ExitStatus"
}, {
"_props": {
"equipmentInclusive": [["5b47574386f77428ca22b336"]],
"IncludeNotEquippedItems": true,
"id": "64cfb3818db9f48b3f0b0a72",
"dynamicLocale": true
},
"_parent": "Equipment"
}
]
}
},
"_parent": "CounterCreator",
"dynamicLocale": true
}
],
"Fail": []
},
"side": "Scav",
"questStatus": {},
"name": "{templateId} name {traderId}",
"note": "{templateId} note {traderId}",
"description": "{templateId} description {traderId} 0",
"successMessageText": "{templateId} successMessageText {traderId} 0",
"failMessageText": "{templateId} failMessageText {traderId} 0",
"startedMessageText": "{templateId} startedMessageText {traderId} 0",
"changeQuestMessageText": "{templateId} changeQuestMessageText {traderId} 0",
"acceptPlayerMessage": "{templateId} acceptPlayerMessage {traderId}",
"declinePlayerMessage": "{templateId} declinePlayerMessage {traderId}",
"completePlayerMessage": "{templateId} completePlayerMessage {traderId}",
"templateId": "{templateId}",
"changeCost": [{
"templateId": "5449016a4bdc2d6f028b456f",
"count": 12000
}
],
"changeStandingCost": 0
}
}, },
"rewards": { "rewards": {
"itemsBlacklist": [ "itemsBlacklist": [

View File

@ -264,8 +264,13 @@ export class RepeatableQuestController
if (location !== ELocationName.ANY) if (location !== ELocationName.ANY)
{ {
questPool.pool.Exploration.locations[location] = repeatableConfig.locations[location]; questPool.pool.Exploration.locations[location] = repeatableConfig.locations[location];
questPool.pool.Pickup.locations[location] = repeatableConfig.locations[location];
} }
} }
// Add "any" to pickup quest pool
questPool.pool.Pickup.locations["any"] = ["any"];
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) for (const probabilityObject of targetsConfig)
@ -299,6 +304,9 @@ export class RepeatableQuestController
}, },
Elimination: { Elimination: {
targets: {} targets: {}
},
Pickup: {
locations: {}
} }
} }
}; };

View File

@ -13,8 +13,10 @@ import {
ICompletionAvailableFor, ICompletionAvailableFor,
IElimination, IElimination,
IEliminationCondition, IEliminationCondition,
IEquipmentConditionProps,
IExploration, IExploration,
IExplorationCondition, IKillConditionProps, IExplorationCondition, IKillConditionProps,
IPickup,
IRepeatableQuest, IReward, IRewards IRepeatableQuest, IReward, IRewards
} from "../models/eft/common/tables/IRepeatableQuests"; } from "../models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem"; import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
@ -104,6 +106,8 @@ export class RepeatableQuestGenerator
return this.generateCompletionQuest(pmcLevel, traderId, repeatableConfig); return this.generateCompletionQuest(pmcLevel, traderId, repeatableConfig);
case "Exploration": case "Exploration":
return this.generateExplorationQuest(pmcLevel, traderId, questTypePool, repeatableConfig); return this.generateExplorationQuest(pmcLevel, traderId, questTypePool, repeatableConfig);
case "Pickup":
return this.generatePickupQuest(pmcLevel, traderId, questTypePool, repeatableConfig);
default: default:
throw new Error(`Unknown mission type ${questType}. Should never be here!`); throw new Error(`Unknown mission type ${questType}. Should never be here!`);
} }
@ -304,7 +308,7 @@ export class RepeatableQuestGenerator
// crazy maximum difficulty will lead to a higher difficulty reward gain factor than 1 // crazy maximum difficulty will lead to a higher difficulty reward gain factor than 1
const difficulty = this.mathUtil.mapToRange(curDifficulty, minDifficulty, maxDifficulty, 0.5, 2); const difficulty = this.mathUtil.mapToRange(curDifficulty, minDifficulty, maxDifficulty, 0.5, 2);
const quest = this.generateRepeatableTemplate("Elimination", traderId,repeatableConfig.side) as IElimination; const quest = this.generateRepeatableTemplate("Elimination", traderId, repeatableConfig.side) as IElimination;
// ASSUMPTION: All fence quests are for scavs // ASSUMPTION: All fence quests are for scavs
if (traderId === Traders.FENCE) if (traderId === Traders.FENCE)
@ -312,15 +316,16 @@ export class RepeatableQuestGenerator
quest.side = "Scav"; quest.side = "Scav";
} }
quest.conditions.AvailableForFinish[0]._props.counter.id = this.objectId.generate(); const availableForFinishCondition = quest.conditions.AvailableForFinish[0];
quest.conditions.AvailableForFinish[0]._props.counter.conditions = []; availableForFinishCondition._props.counter.id = this.objectId.generate();
availableForFinishCondition._props.counter.conditions = [];
if (locationKey !== "any") if (locationKey !== "any")
{ {
quest.conditions.AvailableForFinish[0]._props.counter.conditions.push(this.generateEliminationLocation(locationsConfig[locationKey], allowedWeapon, allowedWeaponsCategory)); availableForFinishCondition._props.counter.conditions.push(this.generateEliminationLocation(locationsConfig[locationKey], allowedWeapon, allowedWeaponsCategory));
} }
quest.conditions.AvailableForFinish[0]._props.counter.conditions.push(this.generateEliminationCondition(targetKey, bodyPartsToClient, distance, allowedWeapon, allowedWeaponsCategory)); availableForFinishCondition._props.counter.conditions.push(this.generateEliminationCondition(targetKey, bodyPartsToClient, distance, allowedWeapon, allowedWeaponsCategory));
quest.conditions.AvailableForFinish[0]._props.value = kills; availableForFinishCondition._props.value = kills;
quest.conditions.AvailableForFinish[0]._props.id = this.objectId.generate(); availableForFinishCondition._props.id = this.objectId.generate();
quest.location = this.getQuestLocationByMapId(locationKey); quest.location = this.getQuestLocationByMapId(locationKey);
quest.rewards = this.generateReward(pmcLevel, Math.min(difficulty, 1), traderId, repeatableConfig); quest.rewards = this.generateReward(pmcLevel, Math.min(difficulty, 1), traderId, repeatableConfig);
@ -660,6 +665,40 @@ export class RepeatableQuestGenerator
return quest; return quest;
} }
protected generatePickupQuest(
pmcLevel: number,
traderId: string,
questTypePool: IQuestTypePool,
repeatableConfig: IRepeatableQuestConfig
): IPickup
{
const pickupConfig = repeatableConfig.questConfig.Pickup;
const quest = this.generateRepeatableTemplate("Pickup", traderId, repeatableConfig.side) as IPickup;
const itemTypeToFetchWithCount = this.randomUtil.getArrayValue(pickupConfig.ItemTypeToFetchWithMaxCount);
const itemCountToFetch = this.randomUtil.randInt(itemTypeToFetchWithCount.minPickupCount, itemTypeToFetchWithCount.maxPickupCount + 1);
// Choose location - doesnt seem to work for anything other than 'any'
//const locationKey: string = this.randomUtil.drawRandomFromDict(questTypePool.pool.Pickup.locations)[0];
//const locationTarget = questTypePool.pool.Pickup.locations[locationKey];
const findCondition = quest.conditions.AvailableForFinish.find(x => x._parent === "FindItem");
findCondition._props.target = [itemTypeToFetchWithCount.itemType];
findCondition._props.value = itemCountToFetch;
const counterCreatorCondition = quest.conditions.AvailableForFinish.find(x => x._parent === "CounterCreator");
//const locationCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Location");
//(locationCondition._props as ILocationConditionProps).target = [...locationTarget];
const equipmentCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Equipment");
(equipmentCondition._props as IEquipmentConditionProps).equipmentInclusive = [[itemTypeToFetchWithCount.itemType]];
// Add rewards
quest.rewards = this.generateReward(pmcLevel, 1, traderId, repeatableConfig);
return quest;
}
/** /**
* Convert a location into an quest code can read (e.g. factory4_day into 55f2d3fd4bdc2d5f408b4567) * Convert a location into an quest code can read (e.g. factory4_day into 55f2d3fd4bdc2d5f408b4567)
* @param locationKey e.g factory4_day * @param locationKey e.g factory4_day

View File

@ -119,7 +119,7 @@ export interface IAvailableForPropsCounter extends IAvailableForProps
type: string type: string
oneSessionOnly: boolean oneSessionOnly: boolean
doNotResetIfCounterCompleted: boolean doNotResetIfCounterCompleted: boolean
counter: ICounter counter?: ICounter
} }
export interface ICounter export interface ICounter
@ -204,6 +204,38 @@ export interface IExplorationCondition extends ICondition
_props: ILocationConditionProps | IExitStatusConditionProps | IExitNameConditionProps _props: ILocationConditionProps | IExitStatusConditionProps | IExitNameConditionProps
} }
// Pickup
export interface IPickup extends IRepeatableQuest
{
conditions: IPickupConditions
}
export interface IPickupConditions extends IConditions
{
AvailableForFinish: IPickupAvailableFor[]
}
export interface IPickupAvailableFor extends IAvailableFor
{
_props: IPickupAvailableForProps
}
export interface IPickupAvailableForProps extends IAvailableForPropsCounter
{
target: string[]
counter?: IPickupCounter
}
export interface IPickupCounter extends ICounter
{
conditions: IPickupCondition[]
}
export interface IPickupCondition extends ICondition
{
_props: IEquipmentConditionProps | ILocationConditionProps | IExitStatusConditionProps
}
// Completion // Completion
export interface ICompletion extends IRepeatableQuest export interface ICompletion extends IRepeatableQuest
{ {
@ -238,6 +270,12 @@ export interface ILocationConditionProps extends IConditionProps
weaponCategories?: string[] weaponCategories?: string[]
} }
export interface IEquipmentConditionProps extends IConditionProps
{
equipmentInclusive: [string[]]
IncludeNotEquippedItems: boolean
}
export interface IKillConditionProps extends IConditionProps export interface IKillConditionProps extends IConditionProps
{ {
target: string target: string

View File

@ -79,6 +79,7 @@ export interface IRepeatableQuestTypesConfig
{ {
Exploration: IExploration Exploration: IExploration
Completion: ICompletion Completion: ICompletion
Pickup: IPickup;
Elimination: IEliminationConfig[] Elimination: IEliminationConfig[]
} }
@ -103,6 +104,18 @@ export interface ICompletion
useWhitelist: boolean useWhitelist: boolean
useBlacklist: boolean useBlacklist: boolean
} }
export interface IPickup
{
ItemTypeToFetchWithMaxCount: IPickupTypeWithMaxCount[]
}
export interface IPickupTypeWithMaxCount
{
itemType: string
maxPickupCount: number
minPickupCount: number
}
export interface IEliminationConfig export interface IEliminationConfig
{ {

View File

@ -10,6 +10,7 @@ export interface IQuestPool
{ {
Exploration: IExplorationPool Exploration: IExplorationPool
Elimination: IEliminationPool Elimination: IEliminationPool
Pickup: IExplorationPool
} }
export interface IExplorationPool export interface IExplorationPool