From 650a3173c8d99d3bd44750223e848236bf572b8a Mon Sep 17 00:00:00 2001 From: Dev Date: Tue, 17 Oct 2023 16:28:48 +0100 Subject: [PATCH] Implement `Pickup` quests for scav It seems like these are scav-only --- project/assets/configs/quest.json | 26 ++++- .../database/templates/repeatableQuests.json | 106 +++++++++++++++++- .../controllers/RepeatableQuestController.ts | 8 ++ .../generators/RepeatableQuestGenerator.ts | 53 +++++++-- .../eft/common/tables/IRepeatableQuests.ts | 40 ++++++- project/src/models/spt/config/IQuestConfig.ts | 13 +++ .../models/spt/repeatable/IQuestTypePool.ts | 1 + 7 files changed, 235 insertions(+), 12 deletions(-) diff --git a/project/assets/configs/quest.json b/project/assets/configs/quest.json index 1d346264..40302033 100644 --- a/project/assets/configs/quest.json +++ b/project/assets/configs/quest.json @@ -11,7 +11,8 @@ "scav": { "elimination": "62825ef60e88d037dc1eb428", "completion": "628f588ebb558574b2260fe5", - "exploration": "62825ef60e88d037dc1eb42c" + "exploration": "62825ef60e88d037dc1eb42c", + "pickup": "628f588ebb558574b2260fe5" } }, "showNonSeasonalEventQuests": false, @@ -1297,8 +1298,10 @@ "name": "Daily_Savage", "side": "Scav", "types": [ + "Exploration", "Elimination", - "Completion" + "Completion", + "Pickup" ], "resetTime": 86400, "numQuests": 1, @@ -1324,7 +1327,7 @@ }, "traderWhitelist": [{ "traderId": "579dc571d53a0658a154fbec", - "questTypes": ["Completion", "Exploration", "Elimination"] + "questTypes": ["Completion", "Exploration", "Elimination", "Pickup"] } ], "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": { "minRequestedAmount": 1, "maxRequestedAmount": 5, diff --git a/project/assets/database/templates/repeatableQuests.json b/project/assets/database/templates/repeatableQuests.json index 6347fad4..c22ffb01 100644 --- a/project/assets/database/templates/repeatableQuests.json +++ b/project/assets/database/templates/repeatableQuests.json @@ -182,7 +182,111 @@ } ], "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": { "itemsBlacklist": [ diff --git a/project/src/controllers/RepeatableQuestController.ts b/project/src/controllers/RepeatableQuestController.ts index e6cb5b40..410d868f 100644 --- a/project/src/controllers/RepeatableQuestController.ts +++ b/project/src/controllers/RepeatableQuestController.ts @@ -264,8 +264,13 @@ export class RepeatableQuestController if (location !== ELocationName.ANY) { 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 targetsConfig = this.repeatableQuestHelper.probabilityObjectArray(eliminationConfig.targets); for (const probabilityObject of targetsConfig) @@ -299,6 +304,9 @@ export class RepeatableQuestController }, Elimination: { targets: {} + }, + Pickup: { + locations: {} } } }; diff --git a/project/src/generators/RepeatableQuestGenerator.ts b/project/src/generators/RepeatableQuestGenerator.ts index 611d6f46..38a97c6b 100644 --- a/project/src/generators/RepeatableQuestGenerator.ts +++ b/project/src/generators/RepeatableQuestGenerator.ts @@ -13,8 +13,10 @@ import { ICompletionAvailableFor, IElimination, IEliminationCondition, + IEquipmentConditionProps, IExploration, IExplorationCondition, IKillConditionProps, + IPickup, IRepeatableQuest, IReward, IRewards } from "../models/eft/common/tables/IRepeatableQuests"; import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem"; @@ -104,6 +106,8 @@ export class RepeatableQuestGenerator return this.generateCompletionQuest(pmcLevel, traderId, repeatableConfig); case "Exploration": return this.generateExplorationQuest(pmcLevel, traderId, questTypePool, repeatableConfig); + case "Pickup": + return this.generatePickupQuest(pmcLevel, traderId, questTypePool, repeatableConfig); default: 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 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 if (traderId === Traders.FENCE) @@ -312,15 +316,16 @@ export class RepeatableQuestGenerator quest.side = "Scav"; } - quest.conditions.AvailableForFinish[0]._props.counter.id = this.objectId.generate(); - quest.conditions.AvailableForFinish[0]._props.counter.conditions = []; + const availableForFinishCondition = quest.conditions.AvailableForFinish[0]; + availableForFinishCondition._props.counter.id = this.objectId.generate(); + availableForFinishCondition._props.counter.conditions = []; 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)); - quest.conditions.AvailableForFinish[0]._props.value = kills; - quest.conditions.AvailableForFinish[0]._props.id = this.objectId.generate(); + availableForFinishCondition._props.counter.conditions.push(this.generateEliminationCondition(targetKey, bodyPartsToClient, distance, allowedWeapon, allowedWeaponsCategory)); + availableForFinishCondition._props.value = kills; + availableForFinishCondition._props.id = this.objectId.generate(); quest.location = this.getQuestLocationByMapId(locationKey); quest.rewards = this.generateReward(pmcLevel, Math.min(difficulty, 1), traderId, repeatableConfig); @@ -660,6 +665,40 @@ export class RepeatableQuestGenerator 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) * @param locationKey e.g factory4_day diff --git a/project/src/models/eft/common/tables/IRepeatableQuests.ts b/project/src/models/eft/common/tables/IRepeatableQuests.ts index f2ee2c2d..76193093 100644 --- a/project/src/models/eft/common/tables/IRepeatableQuests.ts +++ b/project/src/models/eft/common/tables/IRepeatableQuests.ts @@ -119,7 +119,7 @@ export interface IAvailableForPropsCounter extends IAvailableForProps type: string oneSessionOnly: boolean doNotResetIfCounterCompleted: boolean - counter: ICounter + counter?: ICounter } export interface ICounter @@ -204,6 +204,38 @@ export interface IExplorationCondition extends ICondition _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 export interface ICompletion extends IRepeatableQuest { @@ -238,6 +270,12 @@ export interface ILocationConditionProps extends IConditionProps weaponCategories?: string[] } +export interface IEquipmentConditionProps extends IConditionProps +{ + equipmentInclusive: [string[]] + IncludeNotEquippedItems: boolean +} + export interface IKillConditionProps extends IConditionProps { target: string diff --git a/project/src/models/spt/config/IQuestConfig.ts b/project/src/models/spt/config/IQuestConfig.ts index 3f43b8ab..983288b7 100644 --- a/project/src/models/spt/config/IQuestConfig.ts +++ b/project/src/models/spt/config/IQuestConfig.ts @@ -79,6 +79,7 @@ export interface IRepeatableQuestTypesConfig { Exploration: IExploration Completion: ICompletion + Pickup: IPickup; Elimination: IEliminationConfig[] } @@ -103,6 +104,18 @@ export interface ICompletion useWhitelist: boolean useBlacklist: boolean } + +export interface IPickup +{ + ItemTypeToFetchWithMaxCount: IPickupTypeWithMaxCount[] +} + +export interface IPickupTypeWithMaxCount +{ + itemType: string + maxPickupCount: number + minPickupCount: number +} export interface IEliminationConfig { diff --git a/project/src/models/spt/repeatable/IQuestTypePool.ts b/project/src/models/spt/repeatable/IQuestTypePool.ts index e0a38f54..4b2911cf 100644 --- a/project/src/models/spt/repeatable/IQuestTypePool.ts +++ b/project/src/models/spt/repeatable/IQuestTypePool.ts @@ -10,6 +10,7 @@ export interface IQuestPool { Exploration: IExplorationPool Elimination: IEliminationPool + Pickup: IExplorationPool } export interface IExplorationPool