@@ -18,7 +18,12 @@ import { HideoutAreas } from "@spt/models/enums/HideoutAreas";
18
18
import { ItemTpl } from "@spt/models/enums/ItemTpl" ;
19
19
import { QuestStatus } from "@spt/models/enums/QuestStatus" ;
20
20
import { SkillTypes } from "@spt/models/enums/SkillTypes" ;
21
- import { ICultistCircleSettings , IDirectRewardSettings , IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig" ;
21
+ import {
22
+ ICraftTimeThreshhold ,
23
+ ICultistCircleSettings ,
24
+ IDirectRewardSettings ,
25
+ IHideoutConfig ,
26
+ } from "@spt/models/spt/config/IHideoutConfig" ;
22
27
import { ILogger } from "@spt/models/spt/utils/ILogger" ;
23
28
import { EventOutputHolder } from "@spt/routers/EventOutputHolder" ;
24
29
import { ConfigServer } from "@spt/servers/ConfigServer" ;
@@ -92,9 +97,11 @@ export class CircleOfCultistService {
92
97
const hasDirectReward = directRewardSettings ?. reward . length > 0 ;
93
98
94
99
// Get craft time and bonus status
95
- const craftingInfo = hasDirectReward
96
- ? { time : directRewardSettings . craftTimeSeconds , rewardType : CircleRewardType . RANDOM }
97
- : this . getCircleCraftingInfo ( rewardAmountRoubles ) ;
100
+ const craftingInfo = this . getCircleCraftingInfo (
101
+ rewardAmountRoubles ,
102
+ this . hideoutConfig . cultistCircle . craftTimeThreshholds ,
103
+ directRewardSettings ,
104
+ ) ;
98
105
99
106
// Create production in pmc profile
100
107
this . registerCircleOfCultistProduction (
@@ -117,7 +124,7 @@ export class CircleOfCultistService {
117
124
const rewards = hasDirectReward
118
125
? this . getDirectRewards ( sessionId , directRewardSettings , cultistCircleStashId )
119
126
: this . getRewardsWithinBudget (
120
- this . getCultistCircleRewardPool ( sessionId , pmcData , craftingInfo . rewardType ) ,
127
+ this . getCultistCircleRewardPool ( sessionId , pmcData , craftingInfo ) ,
121
128
rewardAmountRoubles ,
122
129
cultistCircleStashId ,
123
130
) ;
@@ -222,53 +229,78 @@ export class CircleOfCultistService {
222
229
* Get the circle craft time as seconds, value is based on reward item value
223
230
* And get the bonus status to determine what tier of reward is given
224
231
* @param rewardAmountRoubles Value of rewards in roubles
225
- * @returns craft time seconds and bonus status
232
+ * @param thresholds Threshold values from config
233
+ * @param directRewardSettings values related to direct reward being given
234
+ * @returns craft time + type of reward + reward details
226
235
*/
227
- protected getCircleCraftingInfo ( rewardAmountRoubles : number ) : ICraftDetails {
228
- // Edge case, check if override exists
229
- if ( this . hideoutConfig . cultistCircle . craftTimeOverride !== - 1 ) {
230
- return { time : this . hideoutConfig . cultistCircle . craftTimeOverride , rewardType : CircleRewardType . RANDOM } ;
236
+ protected getCircleCraftingInfo (
237
+ rewardAmountRoubles : number ,
238
+ thresholds : ICraftTimeThreshhold [ ] ,
239
+ directRewardSettings : IDirectRewardSettings ,
240
+ ) : ICraftDetails {
241
+ const result = { time : - 1 , rewardType : CircleRewardType . RANDOM , rewardDetails : null } ;
242
+
243
+ // Direct reward edge case
244
+ if ( directRewardSettings ) {
245
+ result . time = directRewardSettings . craftTimeSeconds ;
246
+
247
+ return result ;
231
248
}
232
249
233
- const thresholds = this . hideoutConfig . cultistCircle . craftTimeThreshholds ;
234
- const matchingThreshold = thresholds . find (
235
- ( craftThreshold ) => craftThreshold . min <= rewardAmountRoubles && craftThreshold . max >= rewardAmountRoubles ,
236
- ) ;
237
-
238
- // If no threshold fits
239
- if ( ! matchingThreshold ) {
240
- // Sanity check that thresholds exist, if not use 12 hours. Otherwise, use the first set.
241
- let fallbackTimer = 43200 ;
242
- if ( thresholds [ 0 ] ?. craftTimeSeconds ) {
243
- fallbackTimer = thresholds [ 0 ] . craftTimeSeconds ;
244
- }
245
- return { time : fallbackTimer , rewardType : CircleRewardType . RANDOM } ;
246
- }
250
+ // Get the threshold that fits the sacrificed amount inside of its min and max values
251
+ const matchingThreshold = this . getMatchingThreshold ( thresholds , rewardAmountRoubles ) ;
247
252
248
253
// Handle 25% chance if over the highest min threshold for a shorter timer. Live is ~0.43 of the base threshold.
249
- const minThresholds = thresholds . map ( ( a ) => a . min ) ;
250
- const highestThresholdMin = Math . max ( ...minThresholds ) ;
254
+ const thresholdMinValues = thresholds . map ( ( threshold ) => threshold . min ) ;
255
+ const largestThresholdMinValue = Math . max ( ...thresholdMinValues ) ;
251
256
if (
252
- rewardAmountRoubles >= highestThresholdMin &&
257
+ rewardAmountRoubles >= largestThresholdMinValue &&
253
258
Math . random ( ) <= this . hideoutConfig . cultistCircle . bonusChanceMultiplier
254
259
) {
255
- const highestThreshold = thresholds . filter ( ( thresholds ) => thresholds . min === highestThresholdMin ) [ 0 ] ;
256
- return {
257
- time : Math . round (
258
- highestThreshold . craftTimeSeconds * this . hideoutConfig . cultistCircle . bonusAmountMultiplier ,
259
- ) ,
260
- rewardType : CircleRewardType . HIDEOUT ,
261
- } ;
260
+ const highestThreshold = thresholds . filter ( ( thresholds ) => thresholds . min === largestThresholdMinValue ) [ 0 ] ;
261
+
262
+ result . time = Math . round (
263
+ highestThreshold . craftTimeSeconds * this . hideoutConfig . cultistCircle . bonusAmountMultiplier ,
264
+ ) ;
265
+ result . rewardType = CircleRewardType . HIDEOUT_TASK ;
266
+
267
+ return result ;
262
268
}
263
269
264
- // Handle not being in the lowest threshold so qualifying for a "high-value" item
265
- const maxThresholds = thresholds . map ( ( a ) => a . max ) ;
266
- const lowestMax = Math . min ( ...maxThresholds ) ;
267
- if ( rewardAmountRoubles >= lowestMax ) {
268
- return { time : matchingThreshold . craftTimeSeconds , rewardType : CircleRewardType . VALUABLE_RANDOM } ;
270
+ // Edge case, check if override exists, Otherwise use matching threshold craft time
271
+ result . time =
272
+ this . hideoutConfig . cultistCircle . craftTimeOverride !== - 1
273
+ ? this . hideoutConfig . cultistCircle . craftTimeOverride
274
+ : matchingThreshold . craftTimeSeconds ;
275
+
276
+ result . rewardDetails = matchingThreshold ;
277
+
278
+ return result ;
279
+ }
280
+
281
+ protected getMatchingThreshold (
282
+ thresholds : ICraftTimeThreshhold [ ] ,
283
+ rewardAmountRoubles : number ,
284
+ ) : ICraftTimeThreshhold {
285
+ const matchingThreshold = thresholds . find (
286
+ ( craftThreshold ) => craftThreshold . min <= rewardAmountRoubles && craftThreshold . max >= rewardAmountRoubles ,
287
+ ) ;
288
+
289
+ // No matching threshold, make one
290
+ if ( ! matchingThreshold ) {
291
+ // None found, use a defalt
292
+ this . logger . warning ( "Unable to find a matching cultist circle threshold, using fallback of 12 hours" ) ;
293
+
294
+ // Use first threshold value (cheapest) from parameter array, otherwise use 12 hours
295
+ const firstThreshold = thresholds [ 0 ] ;
296
+ const craftTime = firstThreshold ?. craftTimeSeconds
297
+ ? firstThreshold . craftTimeSeconds
298
+ : this . timeUtil . getHoursAsSeconds ( 12 ) ;
299
+
300
+ return { min : firstThreshold ?. min ?? 1 , max : firstThreshold ?. max ?? 34999 , craftTimeSeconds : craftTime } ;
269
301
}
270
302
271
- return { time : matchingThreshold . craftTimeSeconds , rewardType : CircleRewardType . RANDOM } ;
303
+ return matchingThreshold ;
272
304
}
273
305
274
306
/**
@@ -551,7 +583,7 @@ export class CircleOfCultistService {
551
583
* @param rewardType Do we return bonus items (hideout/task items)
552
584
* @returns Array of tpls
553
585
*/
554
- protected getCultistCircleRewardPool ( sessionId : string , pmcData : IPmcData , rewardType : CircleRewardType ) : string [ ] {
586
+ protected getCultistCircleRewardPool ( sessionId : string , pmcData : IPmcData , craftingInfo : ICraftDetails ) : string [ ] {
555
587
const rewardPool = new Set < string > ( ) ;
556
588
const cultistCircleConfig = this . hideoutConfig . cultistCircle ;
557
589
const hideoutDbData = this . databaseService . getHideout ( ) ;
@@ -564,22 +596,17 @@ export class CircleOfCultistService {
564
596
] ;
565
597
566
598
// Hideout and task rewards are ONLY if the bonus is active
567
- switch ( rewardType ) {
568
- case CircleRewardType . RANDOM :
599
+ switch ( craftingInfo . rewardType ) {
600
+ case CircleRewardType . RANDOM : {
569
601
// Just random items so we'll add maxRewardItemCount * 2 amount of random things
570
- this . logger . debug ( "Generating level 0 cultist loot" ) ;
571
- this . getRandomLoot ( rewardPool , itemRewardBlacklist , false ) ;
572
- break ;
573
602
574
- case CircleRewardType . VALUABLE_RANDOM :
575
- // High value loot only we'll add maxRewardItemCount * 2 amount of random things
576
- this . logger . debug ( "Generating level 1 cultist loot" ) ;
577
- this . getRandomLoot ( rewardPool , itemRewardBlacklist , true ) ;
603
+ // Does reward pass the high value threshold
604
+ const isHighValueReward = craftingInfo . rewardDetails . min >= cultistCircleConfig . highValueThresholdRub ;
605
+ this . getRandomLoot ( rewardPool , itemRewardBlacklist , isHighValueReward ) ;
578
606
break ;
579
-
580
- case CircleRewardType . HIDEOUT : {
607
+ }
608
+ case CircleRewardType . HIDEOUT_TASK : {
581
609
// Hideout/Task loot
582
- this . logger . debug ( "Generating level 2 cultist loot" ) ;
583
610
// Add hideout upgrade requirements
584
611
const dbAreas = hideoutDbData . areas ;
585
612
for ( const area of this . getPlayerAccessibleHideoutAreas ( pmcData . Hideout . Areas ) ) {
@@ -717,11 +744,11 @@ export class CircleOfCultistService {
717
744
718
745
export enum CircleRewardType {
719
746
RANDOM = 0 ,
720
- VALUABLE_RANDOM = 1 ,
721
- HIDEOUT = 2 ,
747
+ HIDEOUT_TASK = 1 ,
722
748
}
723
749
724
750
export interface ICraftDetails {
725
751
time : number ;
726
752
rewardType : CircleRewardType ;
753
+ rewardDetails ?: ICraftTimeThreshhold ;
727
754
}
0 commit comments