Skip to content

Commit a7afe1c

Browse files
author
Chomp
committed
Reworked getCircleCraftingInfo and related code areas
Fixed `cultistCircle.craftTimeOverride` property not correctly applying to hideout rewards
1 parent 3f70e6d commit a7afe1c

File tree

1 file changed

+82
-55
lines changed

1 file changed

+82
-55
lines changed

project/src/services/CircleOfCultistService.ts

+82-55
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ import { HideoutAreas } from "@spt/models/enums/HideoutAreas";
1818
import { ItemTpl } from "@spt/models/enums/ItemTpl";
1919
import { QuestStatus } from "@spt/models/enums/QuestStatus";
2020
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";
2227
import { ILogger } from "@spt/models/spt/utils/ILogger";
2328
import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
2429
import { ConfigServer } from "@spt/servers/ConfigServer";
@@ -92,9 +97,11 @@ export class CircleOfCultistService {
9297
const hasDirectReward = directRewardSettings?.reward.length > 0;
9398

9499
// 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+
);
98105

99106
// Create production in pmc profile
100107
this.registerCircleOfCultistProduction(
@@ -117,7 +124,7 @@ export class CircleOfCultistService {
117124
const rewards = hasDirectReward
118125
? this.getDirectRewards(sessionId, directRewardSettings, cultistCircleStashId)
119126
: this.getRewardsWithinBudget(
120-
this.getCultistCircleRewardPool(sessionId, pmcData, craftingInfo.rewardType),
127+
this.getCultistCircleRewardPool(sessionId, pmcData, craftingInfo),
121128
rewardAmountRoubles,
122129
cultistCircleStashId,
123130
);
@@ -222,53 +229,78 @@ export class CircleOfCultistService {
222229
* Get the circle craft time as seconds, value is based on reward item value
223230
* And get the bonus status to determine what tier of reward is given
224231
* @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
226235
*/
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;
231248
}
232249

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);
247252

248253
// 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);
251256
if (
252-
rewardAmountRoubles >= highestThresholdMin &&
257+
rewardAmountRoubles >= largestThresholdMinValue &&
253258
Math.random() <= this.hideoutConfig.cultistCircle.bonusChanceMultiplier
254259
) {
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;
262268
}
263269

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 };
269301
}
270302

271-
return { time: matchingThreshold.craftTimeSeconds, rewardType: CircleRewardType.RANDOM };
303+
return matchingThreshold;
272304
}
273305

274306
/**
@@ -551,7 +583,7 @@ export class CircleOfCultistService {
551583
* @param rewardType Do we return bonus items (hideout/task items)
552584
* @returns Array of tpls
553585
*/
554-
protected getCultistCircleRewardPool(sessionId: string, pmcData: IPmcData, rewardType: CircleRewardType): string[] {
586+
protected getCultistCircleRewardPool(sessionId: string, pmcData: IPmcData, craftingInfo: ICraftDetails): string[] {
555587
const rewardPool = new Set<string>();
556588
const cultistCircleConfig = this.hideoutConfig.cultistCircle;
557589
const hideoutDbData = this.databaseService.getHideout();
@@ -564,22 +596,17 @@ export class CircleOfCultistService {
564596
];
565597

566598
// 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: {
569601
// 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;
573602

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);
578606
break;
579-
580-
case CircleRewardType.HIDEOUT: {
607+
}
608+
case CircleRewardType.HIDEOUT_TASK: {
581609
// Hideout/Task loot
582-
this.logger.debug("Generating level 2 cultist loot");
583610
// Add hideout upgrade requirements
584611
const dbAreas = hideoutDbData.areas;
585612
for (const area of this.getPlayerAccessibleHideoutAreas(pmcData.Hideout.Areas)) {
@@ -717,11 +744,11 @@ export class CircleOfCultistService {
717744

718745
export enum CircleRewardType {
719746
RANDOM = 0,
720-
VALUABLE_RANDOM = 1,
721-
HIDEOUT = 2,
747+
HIDEOUT_TASK = 1,
722748
}
723749

724750
export interface ICraftDetails {
725751
time: number;
726752
rewardType: CircleRewardType;
753+
rewardDetails?: ICraftTimeThreshhold;
727754
}

0 commit comments

Comments
 (0)