diff --git a/project/assets/configs/quest.json b/project/assets/configs/quest.json index 5cd8c554..e500c036 100644 --- a/project/assets/configs/quest.json +++ b/project/assets/configs/quest.json @@ -149,7 +149,7 @@ "items": [2, 4, 5, 5, 5, 5, 5], "reputation": [0.01, 0.01, 0.02, 0.02, 0.03, 0.03, 0.03], "rewardSpread": 0.5, - "skillRewardChance": [0, 0.01, 0.05, 0.1, 0.15, 0.2, 0.2], + "skillRewardChance": [0, 0.01, 0.05, 0.1, 0.1, 0.15, 0.15], "skillPointReward": [10, 15, 20, 25, 30, 35, 40] }, "locations": { @@ -172,10 +172,11 @@ "543be6564bdc2df4348b4568", "5485a8684bdc2da71d8b4567", "590c745b86f7743cc433c5f2", - "5422acb9af1c889c16000029", "57864c322459775490116fbf", "57864a66245977548f04a81f" - ] + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 20 }, { "traderId": "54cb57776803fa99248b456e", "name": "therapist", @@ -189,8 +190,11 @@ "57864c8c245977548867e7f1", "5448e8d04bdc2ddf718b4569", "57864e4c24597754843f8723", - "57864ee62459775490116fc1" - ] + "57864ee62459775490116fc1", + "543be5664bdc2dd4348b4569" + ], + "rewardCanBeWeapon": false, + "weaponRewardChancePercent": 0 }, { "traderId": "58330581ace78e27b8b10cee", "name": "skier", @@ -199,7 +203,6 @@ "5a341c4086f77401f2541505", "5448e8d64bdc2dce718b4568", "5448e8d04bdc2ddf718b4569", - "5422acb9af1c889c16000029", "55818ad54bdc2ddc698b4569", "57864a3d24597754843f8721", "5a341c4686f77469e155819e", @@ -211,19 +214,26 @@ "590c745b86f7743cc433c5f2", "57864a66245977548f04a81f", "5c164d2286f774194c5e69fa" - ] + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 20 }, { "traderId": "5935c25fb3acc3127c3d8cd9", "name": "peacekeeper", "questTypes": ["Completion", "Exploration", "Elimination"], "rewardBaseWhitelist": [ - "5422acb9af1c889c16000029", "543be6564bdc2df4348b4568", "5448e5284bdc2dcb718b4567", "5485a8684bdc2da71d8b4567", "57864a3d24597754843f8721", - "55818af64bdc2d5b648b4570" - ] + "55818af64bdc2d5b648b4570", + "57864e4c24597754843f8723", + "57864a66245977548f04a81f", + "57864ee62459775490116fc1", + "590c745b86f7743cc433c5f2" + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 20 }, { "traderId": "5a7c2eca46aef81a7ca2145d", "name": "mechanic", @@ -234,10 +244,16 @@ "55818b164bdc2ddc698b456c", "55818a684bdc2ddd698b456d", "550aa4cd4bdc2dd8348b456c", - "5422acb9af1c889c16000029", "5485a8684bdc2da71d8b4567", - "55818b224bdc2dde698b456f" - ] + "55818b224bdc2dde698b456f", + "57864a66245977548f04a81f", + "55818af64bdc2d5b648b4570", + "550aa4dd4bdc2dc9348b4569", + "55818a594bdc2db9688b456a", + "55818a104bdc2db9688b4569" + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 20 }, { "traderId": "5ac3b934156ae10c4430e83c", "name": "ragman", @@ -251,7 +267,9 @@ "5448e53e4bdc2d60728b4567", "5448e5284bdc2dcb718b4567", "57864a66245977548f04a81f" - ] + ], + "rewardCanBeWeapon": false, + "weaponRewardChancePercent": 0 }, { "traderId": "5c0647fdd443bc2504c2d371", "name": "jaeger", @@ -268,7 +286,9 @@ "5795f317245977243854e041", "5d650c3e815116009f6201d2", "57864ada245977548638de91" - ] + ], + "rewardCanBeWeapon": false, + "weaponRewardChancePercent": 0 } ], "questConfig": { @@ -799,7 +819,7 @@ "items": [4, 5, 5, 6, 6, 7, 7], "reputation": [0.02, 0.03, 0.04, 0.04, 0.05, 0.05, 0.05], "rewardSpread": 0.5, - "skillRewardChance": [0, 0.05, 0.1, 0.15, 0.25, 0.3, 0.3], + "skillRewardChance": [0, 0.05, 0.1, 0.15, 0.2, 0.2, 0.2], "skillPointReward": [25, 35, 45, 50, 55, 60, 65] }, "locations": { @@ -822,10 +842,11 @@ "543be6564bdc2df4348b4568", "5485a8684bdc2da71d8b4567", "590c745b86f7743cc433c5f2", - "5422acb9af1c889c16000029", "57864c322459775490116fbf", "57864a66245977548f04a81f" - ] + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 25 }, { "traderId": "54cb57776803fa99248b456e", "name": "therapist", @@ -839,8 +860,11 @@ "57864c8c245977548867e7f1", "5448e8d04bdc2ddf718b4569", "57864e4c24597754843f8723", - "57864ee62459775490116fc1" - ] + "57864ee62459775490116fc1", + "543be5664bdc2dd4348b4569" + ], + "rewardCanBeWeapon": false, + "weaponRewardChancePercent": 0 }, { "traderId": "58330581ace78e27b8b10cee", "name": "skier", @@ -849,7 +873,6 @@ "5a341c4086f77401f2541505", "5448e8d64bdc2dce718b4568", "5448e8d04bdc2ddf718b4569", - "5422acb9af1c889c16000029", "55818ad54bdc2ddc698b4569", "57864a3d24597754843f8721", "5a341c4686f77469e155819e", @@ -861,19 +884,26 @@ "590c745b86f7743cc433c5f2", "57864a66245977548f04a81f", "5c164d2286f774194c5e69fa" - ] + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 25 }, { "traderId": "5935c25fb3acc3127c3d8cd9", "name": "peacekeeper", "questTypes": ["Completion", "Exploration", "Elimination"], "rewardBaseWhitelist": [ - "5422acb9af1c889c16000029", "543be6564bdc2df4348b4568", "5448e5284bdc2dcb718b4567", "5485a8684bdc2da71d8b4567", "57864a3d24597754843f8721", - "55818af64bdc2d5b648b4570" - ] + "55818af64bdc2d5b648b4570", + "57864e4c24597754843f8723", + "57864a66245977548f04a81f", + "57864ee62459775490116fc1", + "590c745b86f7743cc433c5f2" + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 25 }, { "traderId": "5a7c2eca46aef81a7ca2145d", "name": "mechanic", @@ -884,10 +914,16 @@ "55818b164bdc2ddc698b456c", "55818a684bdc2ddd698b456d", "550aa4cd4bdc2dd8348b456c", - "5422acb9af1c889c16000029", "5485a8684bdc2da71d8b4567", - "55818b224bdc2dde698b456f" - ] + "55818b224bdc2dde698b456f", + "57864a66245977548f04a81f", + "55818af64bdc2d5b648b4570", + "550aa4dd4bdc2dc9348b4569", + "55818a594bdc2db9688b456a", + "55818a104bdc2db9688b4569" + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 25 }, { "traderId": "5ac3b934156ae10c4430e83c", "name": "ragman", @@ -901,7 +937,9 @@ "5448e53e4bdc2d60728b4567", "5448e5284bdc2dcb718b4567", "57864a66245977548f04a81f" - ] + ], + "rewardCanBeWeapon": false, + "weaponRewardChancePercent": 0 }, { "traderId": "5c0647fdd443bc2504c2d371", "name": "jaeger", @@ -918,14 +956,16 @@ "5795f317245977243854e041", "5d650c3e815116009f6201d2", "57864ada245977548638de91" - ] + ], + "rewardCanBeWeapon": false, + "weaponRewardChancePercent": 0 } ], "questConfig": { "Exploration": { - "possibleSkillRewards": ["Endurance", "Strength", "Vitality"], "maxExtracts": 25, "maxExtractsWithSpecificExit": 12, + "possibleSkillRewards": ["Endurance", "Strength", "Vitality"], "specificExits": { "probability": 0.4, "passageRequirementWhitelist": [ @@ -1538,9 +1578,10 @@ "55818a594bdc2db9688b456a", "57864c8c245977548867e7f1", "5448ecbe4bdc2d60728b4568", - "5422acb9af1c889c16000029", "57864bb7245977548b3b66c2" - ] + ], + "rewardCanBeWeapon": true, + "weaponRewardChancePercent": 10 } ], "questConfig": { diff --git a/project/src/generators/RepeatableQuestGenerator.ts b/project/src/generators/RepeatableQuestGenerator.ts index ecbe4338..e06d464d 100644 --- a/project/src/generators/RepeatableQuestGenerator.ts +++ b/project/src/generators/RepeatableQuestGenerator.ts @@ -907,13 +907,23 @@ export class RepeatableQuestGenerator } rewardIndex++; + const traderWhitelistDetails = repeatableConfig.traderWhitelist.find((x) => x.traderId === traderId); + if (traderWhitelistDetails.rewardCanBeWeapon && this.randomUtil.getChance100(traderWhitelistDetails.weaponRewardChancePercent)) + { + // Add a random default preset weapon as reward + const defaultPresets = Object.values(this.presetHelper.getDefaultPresets()); + const defaultPreset = this.randomUtil.getArrayValue(defaultPresets); + + // use _encyclopedia as its always the base items _tpl, items[0] isnt guaranteed to be base item + rewards.Success.push(this.generateRewardItem(defaultPreset._encyclopedia, 1, rewardIndex, defaultPreset._items)); + rewardIndex++; + } + if (rewardItemPool.length > 0) { - let weaponRewardCount = 0; - for (let i = 0; i < rewardNumItems - 1; i++) + for (let i = 0; i < rewardNumItems; i++) { - let itemCount = 1; - let children: Item[] = null; + let rewardItemStackCount = 1; const itemSelected = rewardItemPool[this.randomUtil.randInt(rewardItemPool.length)]; if (this.itemHelper.isOfBaseclass(itemSelected._id, BaseClasses.AMMO)) { @@ -924,54 +934,23 @@ export class RepeatableQuestGenerator } // Randomise the cartridge count returned - itemCount = this.randomUtil.randInt( + rewardItemStackCount = this.randomUtil.randInt( repeatableConfig.rewardAmmoStackMinSize, itemSelected._props.StackMaxSize, ); } - else if (this.itemHelper.isOfBaseclass(itemSelected._id, BaseClasses.WEAPON)) - { - if (weaponRewardCount >= 1) - { - // Limit weapon rewards to 1 per daily - rewardItemPool = rewardItemPool.filter((x) => - !this.itemHelper.isOfBaseclass(x._id, BaseClasses.WEAPON) - ); - - if (rewardItemPool.length === 0) - { - // No rewards left, break out of loop - break; - } - - // Another weapon chosen, skip - continue; - } - let defaultPreset = this.presetHelper.getDefaultPreset(itemSelected._id); - if (!defaultPreset) - { - // No default for chosen weapon found, get any random default weapon preset - const defaultPresets = Object.values(this.presetHelper.getDefaultPresets()); - defaultPreset = this.randomUtil.getArrayValue(defaultPresets); - } - - children = this.ragfairServerHelper.reparentPresets(defaultPreset._items[0], defaultPreset._items); - weaponRewardCount++; - } // 25% chance to double reward stack (item should be stackable and not weapon) - if (this.increaseRewardItemStackSize(itemSelected)) + if (this.canIncreaseRewardItemStackSize(itemSelected, 70000)) { - itemCount = 2; + rewardItemStackCount = this.getRandomisedRewardItemStackSizeByPrice(itemSelected); } - rewards.Success.push(this.generateRewardItem(itemSelected._id, itemCount, rewardIndex, children)); + rewards.Success.push(this.generateRewardItem(itemSelected._id, rewardItemStackCount, rewardIndex)); rewardIndex++; - const itemCost = (this.itemHelper.isOfBaseclass(itemSelected._id, BaseClasses.WEAPON)) - ? this.itemHelper.getItemMaxPrice(children[0]._tpl) // use if preset is not default : this.itemHelper.getWeaponPresetPrice(children[0], children, this.itemHelper.getStaticItemPrice(itemSelected._id)) - : this.itemHelper.getStaticItemPrice(itemSelected._id); - roublesBudget -= itemCount * itemCost; + const itemCost = this.itemHelper.getStaticItemPrice(itemSelected._id); + roublesBudget -= rewardItemStackCount * itemCost; // If we still have budget narrow down possible items if (roublesBudget > 0) @@ -1018,15 +997,34 @@ export class RepeatableQuestGenerator /** * Should reward item have stack size increased (25% chance) * @param item Item to possibly increase stack size of + * @param maxRoublePriceToStack Maximum rouble price an item can be to still be chosen for stacking * @returns True if it should */ - protected increaseRewardItemStackSize(item: ITemplateItem): boolean + protected canIncreaseRewardItemStackSize(item: ITemplateItem, maxRoublePriceToStack: number): boolean { - return item._props.StackMaxSize > 1 - && !this.itemHelper.isOfBaseclass(item._id, BaseClasses.WEAPON) + return this.itemHelper.getStaticItemPrice(item._id) < maxRoublePriceToStack + && !this.itemHelper.isOfBaseclasses(item._id, [BaseClasses.WEAPON, BaseClasses.AMMO]) && this.randomUtil.getChance100(25); } + /** + * Get a randomised number a reward items stack size should be based on its handbook price + * @param item Reward item to get stack size for + * @returns Stack size value + */ + protected getRandomisedRewardItemStackSizeByPrice(item: ITemplateItem): number + { + const rewardItemPrice = this.itemHelper.getStaticItemPrice(item._id); + if (rewardItemPrice < 3000) { + return this.randomUtil.getArrayValue([2, 3, 4]); + } + else if (rewardItemPrice < 10000) { + return this.randomUtil.getArrayValue([2, 3]); + } + + return 2; + } + /** * Select a number of items that have a colelctive value of the passed in parameter * @param repeatableConfig Config @@ -1071,19 +1069,20 @@ export class RepeatableQuestGenerator * @param {integer} index All rewards will be appended to a list, for unknown reasons the client wants the index * @returns {object} Object of "Reward"-item-type */ - protected generateRewardItem(tpl: string, value: number, index: number, preset = null): IReward + protected generateRewardItem(tpl: string, value: number, index: number, preset: Item[] = null): IReward { const id = this.objectId.generate(); const rewardItem: IReward = { target: id, value: value, type: "Item", index: index }; - const rootItem = { _id: id, _tpl: tpl, upd: { StackObjectsCount: value, SpawnedInSession: true } }; - if (preset) { + const rootItem = preset.find(x => x._tpl === tpl); + rewardItem.target = rootItem._id; // Target property and root items id must match rewardItem.items = this.ragfairServerHelper.reparentPresets(rootItem, preset); } else { + const rootItem = { _id: id, _tpl: tpl, upd: { StackObjectsCount: value, SpawnedInSession: true } }; rewardItem.items = [rootItem]; } return rewardItem; diff --git a/project/src/models/spt/config/IQuestConfig.ts b/project/src/models/spt/config/IQuestConfig.ts index a761743a..147373ea 100644 --- a/project/src/models/spt/config/IQuestConfig.ts +++ b/project/src/models/spt/config/IQuestConfig.ts @@ -77,6 +77,8 @@ export interface ITraderWhitelist traderId: string; questTypes: string[]; rewardBaseWhitelist: string[]; + rewardCanBeWeapon: boolean + weaponRewardChancePercent: number } export interface IRepeatableQuestTypesConfig