diff --git a/project/assets/configs/hideout.json b/project/assets/configs/hideout.json index 3b86438bb..3fa0c11d5 100644 --- a/project/assets/configs/hideout.json +++ b/project/assets/configs/hideout.json @@ -38,34 +38,45 @@ ], "craftTimeOverride": -1, "directRewards": [ - {"reward": ["665ee77ccf2d642e98220bca"], "requiredItems": ["5857a8bc2459772bad15db29"], "craftTimeSeconds": 360 }, - {"reward": ["5c093ca986f7740a1867ab12"], "requiredItems": ["5732ee6a24597719ae0c0281"], "craftTimeSeconds": 3960 }, - {"reward": ["655c669103999d3c810c025b"], "requiredItems": ["635267ab3c89e2112001f826"], "craftTimeSeconds": 3960 }, + {"reward": ["5857a8bc2459772bad15db29"], "requiredItems": ["665ee77ccf2d642e98220bca"], "craftTimeSeconds": 360, + "repeatable": false }, + {"reward": ["5c093ca986f7740a1867ab12"], "requiredItems": ["5732ee6a24597719ae0c0281"], "craftTimeSeconds": 3960, + "repeatable": false }, + {"reward": ["655c669103999d3c810c025b"], "requiredItems": ["635267ab3c89e2112001f826"], "craftTimeSeconds": 3960, + "repeatable": false }, {"reward": ["5fc64ea372b0dd78d51159dc"], "requiredItems": ["635267ab3c89e2112001f826", "635267ab3c89e2112001f826", - "635267ab3c89e2112001f826", "635267ab3c89e2112001f826", "635267ab3c89e2112001f826"], "craftTimeSeconds": 3960 }, + "635267ab3c89e2112001f826", "635267ab3c89e2112001f826", "635267ab3c89e2112001f826"], "craftTimeSeconds": 3960, + "repeatable": false }, {"reward": ["5c0e874186f7745dc7616606", "5c0e842486f77443a74d2976"], "requiredItems": ["66572c82ad599021091c6118"], - "craftTimeSeconds": 3960 }, + "craftTimeSeconds": 3960, "repeatable": false }, {"reward": ["60a7ad3a0c5cb24b0134664a", "60a7ad2a2198820d95707a2e"], "requiredItems": ["66572cbdad599021091c611a"], - "craftTimeSeconds": 3960 }, - {"reward": ["5b3b713c5acfc4330140bd8d"], "requiredItems": ["66572be36a723f7f005a066e"], "craftTimeSeconds": 3960 }, + "craftTimeSeconds": 3960, "repeatable": false }, + {"reward": ["5b3b713c5acfc4330140bd8d"], "requiredItems": ["66572be36a723f7f005a066e"], "craftTimeSeconds": 3960, + "repeatable": false }, {"reward": ["62a09dd4621468534a797ac7", "5bc9b9ecd4351e3bac122519"], "requiredItems": ["66572b8d80b1cd4b6a67847f"], - "craftTimeSeconds": 3960 }, + "craftTimeSeconds": 3960, "repeatable": false }, {"reward": ["5d40407c86f774318526545a", "5d40407c86f774318526545a", "5d40407c86f774318526545a"], "requiredItems": ["655c66e40b2de553b618d4b8"], - "craftTimeSeconds": 3960 }, + "craftTimeSeconds": 3960, "repeatable": false }, {"reward": ["572b7adb24597762ae139821", "56e335e4d2720b6c058b456d"], "requiredItems": ["655c673673a43e23e857aebd"], - "craftTimeSeconds": 3960 }, + "craftTimeSeconds": 3960, "repeatable": false }, {"reward": ["637b60c3b7afa97bfc3d7001", "59e3577886f774176a362503"], "requiredItems": ["655c67782a1356436041c9c5"], - "craftTimeSeconds": 3960 }, - {"reward": ["590c657e86f77412b013051d"], "requiredItems": ["655c652d60d0ac437100fed7"], "craftTimeSeconds": 3960 }, - {"reward": ["5c0e655586f774045612eeb2"], "requiredItems": ["655c663a6689c676ce57af85"], "craftTimeSeconds": 3960 }, - {"reward": ["5d1b376e86f774252519444e"], "requiredItems": ["6582dbf0b8d7830efc45016f"], "craftTimeSeconds": 3960 }, - {"reward": ["62a091170b9d3c46de5b6cf2"], "requiredItems": ["5aa2b986e5b5b00014028f4c"], "craftTimeSeconds": 3960 }, - {"reward": ["62a0a098de7ac8199358053b"], "requiredItems": ["5c13cd2486f774072c757944"], "craftTimeSeconds": 3960 }, + "craftTimeSeconds": 3960, "repeatable": false }, + {"reward": ["590c657e86f77412b013051d"], "requiredItems": ["655c652d60d0ac437100fed7"], "craftTimeSeconds": 3960, + "repeatable": false }, + {"reward": ["5c0e655586f774045612eeb2"], "requiredItems": ["655c663a6689c676ce57af85"], "craftTimeSeconds": 3960, + "repeatable": false }, + {"reward": ["5d1b376e86f774252519444e"], "requiredItems": ["6582dbf0b8d7830efc45016f"], "craftTimeSeconds": 3960, + "repeatable": true }, + {"reward": ["62a091170b9d3c46de5b6cf2"], "requiredItems": ["5aa2b986e5b5b00014028f4c"], "craftTimeSeconds": 3960, + "repeatable": true }, + {"reward": ["62a0a098de7ac8199358053b"], "requiredItems": ["5c13cd2486f774072c757944"], "craftTimeSeconds": 3960, + "repeatable": true }, {"reward": ["5d1b392c86f77425243e98fe", "5d1b392c86f77425243e98fe"], "requiredItems": ["5a0c27731526d80618476ac4"], - "craftTimeSeconds": 3960 }, + "craftTimeSeconds": 3960, "repeatable": true }, {"reward": ["5e2aedd986f7746d404f3aa4", "5e2aedd986f7746d404f3aa4", "5c12620d86f7743f8b198b72", "5c12620d86f7743f8b198b72"], - "requiredItems": ["59faff1d86f7746c51718c9c"], "craftTimeSeconds": 39960 }, - {"reward": ["6389c8c5dbfd5e4b95197e6b"], "requiredItems": ["5c0530ee86f774697952d952"], "craftTimeSeconds": 39960 } + "requiredItems": ["59faff1d86f7746c51718c9c"], "craftTimeSeconds": 39960, "repeatable": true }, + {"reward": ["6389c8c5dbfd5e4b95197e6b"], "requiredItems": ["5c0530ee86f774697952d952"], "craftTimeSeconds": 39960, + "repeatable": true } ], "directRewardStackSize": { "exampleParentId": { "min": 1000, "max": 50000 } diff --git a/project/src/controllers/GameController.ts b/project/src/controllers/GameController.ts index 544ac767c..d1d4bcb82 100644 --- a/project/src/controllers/GameController.ts +++ b/project/src/controllers/GameController.ts @@ -117,6 +117,11 @@ export class GameController { fullProfile.spt.migrations = {}; } + // Need to track one time use cultist rewards + if (typeof fullProfile.spt.cultistRewards === "undefined") { + fullProfile.spt.cultistRewards = []; + } + //3.9 migrations if (fullProfile.spt.version.includes("3.9.") && !fullProfile.spt.migrations["39x"]) { // Check every item has a valid mongoid diff --git a/project/src/models/eft/profile/ISptProfile.ts b/project/src/models/eft/profile/ISptProfile.ts index 1a460b738..0096d4951 100644 --- a/project/src/models/eft/profile/ISptProfile.ts +++ b/project/src/models/eft/profile/ISptProfile.ts @@ -192,6 +192,8 @@ export interface ISpt { freeRepeatableRefreshUsedCount?: Record; /** When was a profile migrated, value is timestamp */ migrations?: Record; + /** Cultist circle rewards received that are one time use */ + cultistRewards?: string[][]; } export interface IModDetails { diff --git a/project/src/models/spt/config/IHideoutConfig.ts b/project/src/models/spt/config/IHideoutConfig.ts index c7e527420..09c2e0e3d 100644 --- a/project/src/models/spt/config/IHideoutConfig.ts +++ b/project/src/models/spt/config/IHideoutConfig.ts @@ -46,4 +46,5 @@ export interface IDirectRewardSettings { reward: string[]; requiredItems: string[]; craftTimeSeconds: number; + repeatable: boolean; } diff --git a/project/src/services/CircleOfCultistService.ts b/project/src/services/CircleOfCultistService.ts index d2a2632cf..4440b47e2 100644 --- a/project/src/services/CircleOfCultistService.ts +++ b/project/src/services/CircleOfCultistService.ts @@ -96,7 +96,7 @@ export class CircleOfCultistService { const rewardAmountRoubles = sacrificedItemCostRoubles * rewardAmountMultiplier; // Check if it matches any direct swap recipes - const directRewardSettings = this.checkForDirectReward(sacrificedItems); + const directRewardSettings = this.checkForDirectReward(sessionId, sacrificedItems); const hasDirectReward = directRewardSettings?.reward.length > 0; // Get craft time and bonus status @@ -124,7 +124,7 @@ export class CircleOfCultistService { } const rewards = hasDirectReward - ? this.getDirectRewards(directRewardSettings, cultistCircleStashId) + ? this.getDirectRewards(sessionId, directRewardSettings, cultistCircleStashId) : this.getRewardsWithinBudget( this.getCultistCircleRewardPool(sessionId, pmcData, craftingInfo.bonus), rewardAmountRoubles, @@ -357,14 +357,20 @@ export class CircleOfCultistService { /** * Get direct rewards + * @param sessionID sessionID * @param directReward Items sacrificed * @param cultistCircleStashId Id of stash item * @returns The reward object */ - protected getDirectRewards(directReward: IDirectRewardSettings, cultistCircleStashId: string): IItem[][] { + protected getDirectRewards(sessionID: string, directReward: IDirectRewardSettings, cultistCircleStashId: string): IItem[][] { // Prep rewards array (reward can be item with children, hence array of arrays) const rewards: IItem[][] = []; + // Handle special case of tagilla helmets + if (directReward.reward.includes("60a7ad3a0c5cb24b0134664a")) { + directReward.reward = [directReward.reward[Math.round(Math.random())]] + } + // Loop because these can include multiple rewards for (const reward of directReward.reward) { const stackSize = this.getExplicitRewardBaseTypeStackSize(reward); @@ -380,20 +386,34 @@ export class CircleOfCultistService { }; rewards.push([rewardItem]); } + // Handle storing non-repeatable rewards + const fullProfile = this.profileHelper.getFullProfile(sessionID); + if (directReward.repeatable === false) { + fullProfile.spt.cultistRewards.push(directReward.reward) + } return rewards; } /** * Check for direct rewards from what player sacrificed + * @param sessionID sessionID * @param sacrificedItems Items sacrificed * @returns Direct reward items to send to player */ - protected checkForDirectReward(sacrificedItems: IItem[]): IDirectRewardSettings { + protected checkForDirectReward(sessionID: string, sacrificedItems: IItem[]): IDirectRewardSettings { // Make an array of sacrificed tpl's const sacrificedItemTpls = sacrificedItems.map((item) => item._tpl); // Loop possible rewards + directRewards: for (const directReward of this.hideoutConfig.cultistCircle.directRewards) { + // Check if this is a one time reward that has already been received + const fullProfile = this.profileHelper.getFullProfile(sessionID); + for (const pastReward of fullProfile.spt.cultistRewards) { + if (this.compareArrays(directReward.reward, pastReward)) { + continue directRewards; + } + } // Does sacrificed item match one of the dictionary keys // yes === its a direct reward if (this.compareArrays(directReward.requiredItems, sacrificedItemTpls)) { @@ -476,7 +496,6 @@ export class CircleOfCultistService { const itemRewardBlacklist = [ ...this.seasonalEventService.getInactiveSeasonalEventItems(), ...this.itemFilterService.getItemRewardBlacklist(), - ...this.itemFilterService.getBossItems(), ...cultistCircleConfig.rewardItemBlacklist, ];