mirror of
https://github.com/sp-tarkov/server.git
synced 2025-02-12 16:10:43 -05:00
Cultist circle improvements (#973)
Brings Cultist Cicle rewards closer to live behavior. --------- Co-authored-by: Bob S <shibdib@users.noreply.github.com> Co-authored-by: Chomp <dev@dev.sp-tarkov.com>
This commit is contained in:
parent
d34eca32bb
commit
d2b7baa8b6
@ -16,68 +16,70 @@
|
||||
"min": 0.7,
|
||||
"max": 1.4
|
||||
},
|
||||
"bonusChanceMultiplier": 0.25,
|
||||
"bonusAmountMultiplier": 0.43,
|
||||
"highValueThresholdRub": 70000,
|
||||
"hideoutTaskRewardTimeSeconds": 21600,
|
||||
"hideoutCraftSacrificeThresholdRub": 400000,
|
||||
"craftTimeThreshholds": [
|
||||
{
|
||||
"min": 1,
|
||||
"max": 25000,
|
||||
"timeSeconds": 21600
|
||||
"max": 350000,
|
||||
"craftTimeSeconds": 43200
|
||||
},
|
||||
{
|
||||
"min": 25001,
|
||||
"min": 350001,
|
||||
"max": 399999,
|
||||
"craftTimeSeconds": 50400
|
||||
},
|
||||
{
|
||||
"min": 400000,
|
||||
"max": 99999999,
|
||||
"timeSeconds": 43200
|
||||
"craftTimeSeconds": 50400
|
||||
}
|
||||
],
|
||||
"craftTimeOverride": 40,
|
||||
"directRewards": {
|
||||
"66572c82ad599021091c6118": { "rewardTpls": ["5c0e874186f7745dc7616606"], "craftTimeSeconds": 3960 },
|
||||
"5aa2b986e5b5b00014028f4c": { "rewardTpls": ["62a091170b9d3c46de5b6cf2"], "craftTimeSeconds": 3960 },
|
||||
"655c663a6689c676ce57af85": { "rewardTpls": ["5c0e655586f774045612eeb2"], "craftTimeSeconds": 3960 },
|
||||
"655c652d60d0ac437100fed7": { "rewardTpls": ["590c657e86f77412b013051d"], "craftTimeSeconds": 3960 },
|
||||
"655c67782a1356436041c9c5": {
|
||||
"rewardTpls": ["59e3577886f774176a362503", "5ed5166ad380ab312177c100"],
|
||||
"craftTimeSeconds": 3960
|
||||
},
|
||||
"655c673673a43e23e857aebd": {
|
||||
"rewardTpls": ["572b7adb24597762ae139821", "56e335e4d2720b6c058b456d"],
|
||||
"craftTimeSeconds": 3960
|
||||
},
|
||||
"655c66e40b2de553b618d4b8": {
|
||||
"rewardTpls": ["5d40407c86f774318526545a", "5d40407c86f774318526545a", "5d40407c86f774318526545a"],
|
||||
"craftTimeSeconds": 3960
|
||||
},
|
||||
"6582dbf0b8d7830efc45016f": { "rewardTpls": ["5d1b376e86f774252519444e"], "craftTimeSeconds": 3960 },
|
||||
"66572be36a723f7f005a066e": { "rewardTpls": ["5b3b713c5acfc4330140bd8d"], "craftTimeSeconds": 3960 },
|
||||
"655c669103999d3c810c025b": {
|
||||
"rewardTpls": ["635267ab3c89e2112001f826", "5fc64ea372b0dd78d51159dc"],
|
||||
"craftTimeSeconds": 3960
|
||||
},
|
||||
"66572cbdad599021091c611a": { "rewardTpls": ["60a7ad2a2198820d95707a2e"], "craftTimeSeconds": 3960 },
|
||||
"5c0530ee86f774697952d952": { "rewardTpls": ["6389c8c5dbfd5e4b95197e6b"], "craftTimeSeconds": 43260 },
|
||||
"66572b8d80b1cd4b6a67847f": {
|
||||
"rewardTpls": ["5bc9b9ecd4351e3bac122519", "62a09dd4621468534a797ac7"],
|
||||
"craftTimeSeconds": 3960
|
||||
},
|
||||
"5c093ca986f7740a1867ab12": { "rewardTpls": ["5732ee6a24597719ae0c0281"], "craftTimeSeconds": 3960 },
|
||||
"665ee77ccf2d642e98220bca": { "rewardTpls": ["5857a8bc2459772bad15db29"], "craftTimeSeconds": 360 },
|
||||
"59faff1d86f7746c51718c9c": {
|
||||
"rewardTpls": [
|
||||
"5c12620d86f7743f8b198b72",
|
||||
"5c12620d86f7743f8b198b72",
|
||||
"5e2aedd986f7746d404f3aa4",
|
||||
"5e2aedd986f7746d404f3aa4"
|
||||
],
|
||||
"craftTimeSeconds": 43260
|
||||
},
|
||||
"655c663a6689c676ce57af85": { "rewardTpls": ["5c0e655586f774045612eeb2"], "craftTimeSeconds": 3960 },
|
||||
"5aa2b986e5b5b00014028f4c": { "rewardTpls": ["62a091170b9d3c46de5b6cf2"], "craftTimeSeconds": 3960 },
|
||||
"5c13cd2486f774072c757944": { "rewardTpls": ["62a0a098de7ac8199358053b"], "craftTimeSeconds": 3960 },
|
||||
"5a0c27731526d80618476ac4": {
|
||||
"rewardTpls": ["5d1b392c86f77425243e98fe", "5d1b392c86f77425243e98fe"],
|
||||
"craftTimeSeconds": 3960
|
||||
},
|
||||
"5c0530ee86f774697952d952": { "rewardTpls": ["6389c8c5dbfd5e4b95197e6b"], "craftTimeSeconds": 39960 }
|
||||
},
|
||||
"craftTimeOverride": -1,
|
||||
"directRewards": [
|
||||
{"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,
|
||||
"repeatable": false },
|
||||
{"reward": ["5c0e874186f7745dc7616606", "5c0e842486f77443a74d2976"], "requiredItems": ["66572c82ad599021091c6118"],
|
||||
"craftTimeSeconds": 3960, "repeatable": false },
|
||||
{"reward": ["60a7ad3a0c5cb24b0134664a", "60a7ad2a2198820d95707a2e"], "requiredItems": ["66572cbdad599021091c611a"],
|
||||
"craftTimeSeconds": 3960, "repeatable": false },
|
||||
{"reward": ["5b3b713c5acfc4330140bd8d"], "requiredItems": ["66572be36a723f7f005a066e"], "craftTimeSeconds": 3960,
|
||||
"repeatable": false },
|
||||
{"reward": ["62a09dd4621468534a797ac7", "5bc9b9ecd4351e3bac122519"], "requiredItems": ["66572b8d80b1cd4b6a67847f"],
|
||||
"craftTimeSeconds": 3960, "repeatable": false },
|
||||
{"reward": ["5d40407c86f774318526545a", "5d40407c86f774318526545a", "5d40407c86f774318526545a"], "requiredItems": ["655c66e40b2de553b618d4b8"],
|
||||
"craftTimeSeconds": 3960, "repeatable": false },
|
||||
{"reward": ["572b7adb24597762ae139821", "56e335e4d2720b6c058b456d"], "requiredItems": ["655c673673a43e23e857aebd"],
|
||||
"craftTimeSeconds": 3960, "repeatable": false },
|
||||
{"reward": ["637b60c3b7afa97bfc3d7001", "59e3577886f774176a362503"], "requiredItems": ["655c67782a1356436041c9c5"],
|
||||
"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, "repeatable": true },
|
||||
{"reward": ["5e2aedd986f7746d404f3aa4", "5e2aedd986f7746d404f3aa4", "5c12620d86f7743f8b198b72", "5c12620d86f7743f8b198b72"],
|
||||
"requiredItems": ["59faff1d86f7746c51718c9c"], "craftTimeSeconds": 39960, "repeatable": true },
|
||||
{"reward": ["6389c8c5dbfd5e4b95197e6b"], "requiredItems": ["5c0530ee86f774697952d952"], "craftTimeSeconds": 39960,
|
||||
"repeatable": true }
|
||||
],
|
||||
"directRewardStackSize": {
|
||||
"exampleParentId": { "min": 1000, "max": 50000 }
|
||||
},
|
||||
|
@ -117,6 +117,11 @@ export class GameController {
|
||||
fullProfile.spt.migrations = {};
|
||||
}
|
||||
|
||||
// Track one time use cultist rewards
|
||||
if (typeof fullProfile.spt.cultistRewards === "undefined") {
|
||||
fullProfile.spt.cultistRewards = new Map();
|
||||
}
|
||||
|
||||
//3.9 migrations
|
||||
if (fullProfile.spt.version.includes("3.9.") && !fullProfile.spt.migrations["39x"]) {
|
||||
// Check every item has a valid mongoid
|
||||
|
@ -847,6 +847,9 @@ export class LocationLootGenerator {
|
||||
throw new Error(`Item for tpl ${chosenComposedKey} was not found in the spawn point`);
|
||||
}
|
||||
const itemTemplate = this.itemHelper.getItem(chosenTpl)[1];
|
||||
if (!itemTemplate) {
|
||||
this.logger.error(`Item tpl: ${chosenTpl} cannot be found in database`);
|
||||
}
|
||||
|
||||
// Item array to return
|
||||
const itemWithMods: IItem[] = [];
|
||||
|
@ -192,6 +192,14 @@ export interface ISpt {
|
||||
freeRepeatableRefreshUsedCount?: Record<string, number>;
|
||||
/** When was a profile migrated, value is timestamp */
|
||||
migrations?: Record<string, number>;
|
||||
/** Cultist circle rewards received that are one time use, key (md5) is a combination of sacrificed + reward items */
|
||||
cultistRewards?: Map<string, IAcceptedCultistReward>;
|
||||
}
|
||||
|
||||
export interface IAcceptedCultistReward {
|
||||
timestamp: number;
|
||||
sacrificeItems: string[];
|
||||
rewardItems: string[];
|
||||
}
|
||||
|
||||
export interface IModDetails {
|
||||
|
@ -20,11 +20,21 @@ export interface ICultistCircleSettings {
|
||||
maxRewardItemCount: number;
|
||||
maxAttemptsToPickRewardsWithinBudget: number;
|
||||
rewardPriceMultiplerMinMax: MinMax;
|
||||
/** The odds that meeting the highest threshold gives you a bonus to crafting time */
|
||||
bonusAmountMultiplier: number;
|
||||
bonusChanceMultiplier: number;
|
||||
/** What is considered a "high-value" item */
|
||||
highValueThresholdRub: number;
|
||||
/** Hideout/task reward crafts have a unique craft time */
|
||||
hideoutTaskRewardTimeSeconds: number;
|
||||
/** Rouble amount player needs to sacrifice to get chance of hideout/task rewards */
|
||||
hideoutCraftSacrificeThresholdRub: number;
|
||||
craftTimeThreshholds: ICraftTimeThreshhold[];
|
||||
/** -1 means no override */
|
||||
/** -1 means no override, value in seconds */
|
||||
craftTimeOverride: number;
|
||||
/** Specific reward pool when player sacrificed one specific item */
|
||||
directRewards: Record<string, IDirectRewardSettings>;
|
||||
/** Specific reward pool when player sacrifice specific item(s) */
|
||||
directRewards: IDirectRewardSettings[];
|
||||
/** Overrides for reward stack sizes, keyed by item tpl */
|
||||
directRewardStackSize: Record<string, MinMax>;
|
||||
/** Item tpls to exclude from the reward pool */
|
||||
rewardItemBlacklist: string[];
|
||||
@ -38,6 +48,9 @@ export interface ICraftTimeThreshhold extends MinMax {
|
||||
}
|
||||
|
||||
export interface IDirectRewardSettings {
|
||||
rewardTpls: string[];
|
||||
reward: string[];
|
||||
requiredItems: string[];
|
||||
craftTimeSeconds: number;
|
||||
/** Is the reward a one time reward or can it be given multiple times */
|
||||
repeatable: boolean;
|
||||
}
|
||||
|
@ -3,24 +3,27 @@ import { InventoryHelper } from "@spt/helpers/InventoryHelper";
|
||||
import { ItemHelper } from "@spt/helpers/ItemHelper";
|
||||
import { PresetHelper } from "@spt/helpers/PresetHelper";
|
||||
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
|
||||
import { QuestHelper } from "@spt/helpers/QuestHelper";
|
||||
import { IPmcData } from "@spt/models/eft/common/IPmcData";
|
||||
import { IBotHideoutArea } from "@spt/models/eft/common/tables/IBotBase";
|
||||
import { IItem } from "@spt/models/eft/common/tables/IItem";
|
||||
import { IStageRequirement } from "@spt/models/eft/hideout/IHideoutArea";
|
||||
import { IHideoutCircleOfCultistProductionStartRequestData } from "@spt/models/eft/hideout/IHideoutCircleOfCultistProductionStartRequestData";
|
||||
import {
|
||||
IHideoutProduction,
|
||||
IHideoutProductionData,
|
||||
IRequirement,
|
||||
IRequirementBase,
|
||||
} from "@spt/models/eft/hideout/IHideoutProduction";
|
||||
import { IRequirement, IRequirementBase } from "@spt/models/eft/hideout/IHideoutProduction";
|
||||
import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse";
|
||||
import { IAcceptedCultistReward } from "@spt/models/eft/profile/ISptProfile";
|
||||
import { BaseClasses } from "@spt/models/enums/BaseClasses";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
import { HideoutAreas } from "@spt/models/enums/HideoutAreas";
|
||||
import { ItemTpl } from "@spt/models/enums/ItemTpl";
|
||||
import { QuestStatus } from "@spt/models/enums/QuestStatus";
|
||||
import { SkillTypes } from "@spt/models/enums/SkillTypes";
|
||||
import { IDirectRewardSettings, IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig";
|
||||
import {
|
||||
ICraftTimeThreshhold,
|
||||
ICultistCircleSettings,
|
||||
IDirectRewardSettings,
|
||||
IHideoutConfig,
|
||||
} from "@spt/models/spt/config/IHideoutConfig";
|
||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { EventOutputHolder } from "@spt/routers/EventOutputHolder";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
@ -50,6 +53,7 @@ export class CircleOfCultistService {
|
||||
@inject("ProfileHelper") protected profileHelper: ProfileHelper,
|
||||
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
|
||||
@inject("HideoutHelper") protected hideoutHelper: HideoutHelper,
|
||||
@inject("QuestHelper") protected questHelper: QuestHelper,
|
||||
@inject("DatabaseService") protected databaseService: DatabaseService,
|
||||
@inject("ItemFilterService") protected itemFilterService: ItemFilterService,
|
||||
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
||||
@ -74,7 +78,7 @@ export class CircleOfCultistService {
|
||||
): IItemEventRouterResponse {
|
||||
const cultistCircleStashId = pmcData.Inventory.hideoutAreaStashes[HideoutAreas.CIRCLE_OF_CULTISTS];
|
||||
|
||||
// Sparse, just has id
|
||||
// `cultistRecipes` just has single recipeId
|
||||
const cultistCraftData = this.databaseService.getHideout().production.cultistRecipes[0];
|
||||
const sacrificedItems: IItem[] = this.getSacrificedItems(pmcData);
|
||||
const sacrificedItemCostRoubles = sacrificedItems.reduce(
|
||||
@ -82,24 +86,22 @@ export class CircleOfCultistService {
|
||||
0,
|
||||
);
|
||||
|
||||
// Get a randomised value to multiply the sacrificed rouble cost by
|
||||
let rewardAmountMultiplier = this.randomUtil.getFloat(
|
||||
this.hideoutConfig.cultistCircle.rewardPriceMultiplerMinMax.min,
|
||||
this.hideoutConfig.cultistCircle.rewardPriceMultiplerMinMax.max,
|
||||
);
|
||||
|
||||
// Adjust the above value generated by the players hideout mgmt skill
|
||||
const hideoutManagementSkill = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.HIDEOUT_MANAGEMENT);
|
||||
if (hideoutManagementSkill) {
|
||||
rewardAmountMultiplier *= 1 + hideoutManagementSkill.Progress / 10000; // 5100 becomes 0.51, add 1 to it, 1.51, multiply the bonus by it (e.g. 1.2 x 1.51)
|
||||
}
|
||||
const rewardAmountMultiplier = this.getRewardAmountMultipler(pmcData, this.hideoutConfig.cultistCircle);
|
||||
|
||||
// Get the rouble amount we generate rewards with from cost of sacrified items * above multipler
|
||||
const rewardAmountRoubles = sacrificedItemCostRoubles * rewardAmountMultiplier;
|
||||
const rewardAmountRoubles = Math.round(sacrificedItemCostRoubles * rewardAmountMultiplier);
|
||||
|
||||
// Has player sacrified a single item in directReward dict
|
||||
const directRewardSettings = this.hideoutConfig.cultistCircle.directRewards[sacrificedItems[0]._tpl];
|
||||
const hasSacrificedSingleItemFlaggedInConfig = sacrificedItems.length === 1 && !!directRewardSettings;
|
||||
// Check if it matches any direct swap recipes
|
||||
const directRewardsCache = this.generateSacrificedItemsCache(this.hideoutConfig.cultistCircle.directRewards);
|
||||
const directRewardSettings = this.checkForDirectReward(sessionId, sacrificedItems, directRewardsCache);
|
||||
const hasDirectReward = directRewardSettings?.reward.length > 0;
|
||||
|
||||
// Get craft time and bonus status
|
||||
const craftingInfo = this.getCircleCraftingInfo(
|
||||
rewardAmountRoubles,
|
||||
this.hideoutConfig.cultistCircle,
|
||||
directRewardSettings,
|
||||
);
|
||||
|
||||
// Create production in pmc profile
|
||||
this.registerCircleOfCultistProduction(
|
||||
@ -107,31 +109,31 @@ export class CircleOfCultistService {
|
||||
pmcData,
|
||||
cultistCraftData._id,
|
||||
sacrificedItems,
|
||||
rewardAmountRoubles,
|
||||
directRewardSettings,
|
||||
craftingInfo.time,
|
||||
);
|
||||
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
// Remove sacrified items
|
||||
// Remove sacrificed items from circle inventory
|
||||
for (const item of sacrificedItems) {
|
||||
if (item.slotId === CircleOfCultistService.circleOfCultistSlotId) {
|
||||
this.inventoryHelper.removeItem(pmcData, item._id, sessionId, output);
|
||||
}
|
||||
}
|
||||
|
||||
let rewards: IItem[][];
|
||||
if (hasSacrificedSingleItemFlaggedInConfig) {
|
||||
rewards = this.getExplicitRewards(directRewardSettings, cultistCircleStashId);
|
||||
} else {
|
||||
const rewardItemPool = this.getCultistCircleRewardPool(sessionId, pmcData);
|
||||
rewards = this.getRewardsWithinBudget(rewardItemPool, rewardAmountRoubles, cultistCircleStashId);
|
||||
}
|
||||
const rewards = hasDirectReward
|
||||
? this.getDirectRewards(sessionId, directRewardSettings, cultistCircleStashId)
|
||||
: this.getRewardsWithinBudget(
|
||||
this.getCultistCircleRewardPool(sessionId, pmcData, craftingInfo, this.hideoutConfig.cultistCircle),
|
||||
rewardAmountRoubles,
|
||||
cultistCircleStashId,
|
||||
this.hideoutConfig.cultistCircle,
|
||||
);
|
||||
|
||||
// Get the container grid for cultist stash area
|
||||
const cultistStashDbItem = this.itemHelper.getItem(ItemTpl.HIDEOUTAREACONTAINER_CIRCLEOFCULTISTS_STASH_1);
|
||||
|
||||
// Ensure items fit into container
|
||||
// Ensure rewards fit into container
|
||||
const containerGrid = this.inventoryHelper.getContainerSlotMap(cultistStashDbItem[1]._id);
|
||||
const canAddToContainer = this.inventoryHelper.canPlaceItemsInContainer(
|
||||
this.cloner.clone(containerGrid), // MUST clone grid before passing in as function modifies grid
|
||||
@ -146,7 +148,6 @@ export class CircleOfCultistService {
|
||||
cultistCircleStashId,
|
||||
CircleOfCultistService.circleOfCultistSlotId,
|
||||
);
|
||||
|
||||
// Add item + mods to output and profile inventory
|
||||
output.profileChanges[sessionId].items.new.push(...itemToAdd);
|
||||
pmcData.Inventory.items.push(...itemToAdd);
|
||||
@ -160,29 +161,60 @@ export class CircleOfCultistService {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a map of the possible direct rewards, keyed by the items needed to be sacrificed
|
||||
* @param directRewards Direct rewards array from hideout config
|
||||
* @returns Map
|
||||
*/
|
||||
protected generateSacrificedItemsCache(directRewards: IDirectRewardSettings[]): Map<string, IDirectRewardSettings> {
|
||||
const result = new Map<string, IDirectRewardSettings>();
|
||||
for (const rewardSettings of directRewards) {
|
||||
const key = this.hashUtil.generateMd5ForData(rewardSettings.requiredItems.sort().join(","));
|
||||
result.set(key, rewardSettings);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reward amount multiple value based on players hideout management skill + configs rewardPriceMultiplerMinMax values
|
||||
* @param pmcData Player profile
|
||||
* @param cultistCircleSettings Circle config settings
|
||||
* @returns Reward Amount Multipler
|
||||
*/
|
||||
protected getRewardAmountMultipler(pmcData: IPmcData, cultistCircleSettings: ICultistCircleSettings): number {
|
||||
// Get a randomised value to multiply the sacrificed rouble cost by
|
||||
let rewardAmountMultiplier = this.randomUtil.getFloat(
|
||||
cultistCircleSettings.rewardPriceMultiplerMinMax.min,
|
||||
cultistCircleSettings.rewardPriceMultiplerMinMax.max,
|
||||
);
|
||||
|
||||
// Adjust value generated by the players hideout management skill
|
||||
const hideoutManagementSkill = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.HIDEOUT_MANAGEMENT);
|
||||
if (hideoutManagementSkill) {
|
||||
rewardAmountMultiplier *= 1 + hideoutManagementSkill.Progress / 10000; // 5100 becomes 0.51, add 1 to it, 1.51, multiply the bonus by it (e.g. 1.2 x 1.51)
|
||||
}
|
||||
|
||||
return rewardAmountMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register production inside player profile
|
||||
* @param sessionId Session id
|
||||
* @param pmcData Player profile
|
||||
* @param recipeId Recipe id
|
||||
* @param sacrificedItems Items player sacrificed
|
||||
* @param rewardAmountRoubles Rouble amount to reward player in items with
|
||||
* @param directRewardSettings OPTIONAL: If craft is giving direct rewards
|
||||
* @param craftingTime How long the ritual should take
|
||||
*/
|
||||
protected registerCircleOfCultistProduction(
|
||||
sessionId: string,
|
||||
pmcData: IPmcData,
|
||||
recipeId: string,
|
||||
sacrificedItems: IItem[],
|
||||
rewardAmountRoubles: number,
|
||||
directRewardSettings?: IDirectRewardSettings,
|
||||
craftingTime: number,
|
||||
): void {
|
||||
// Create circle production/craft object to add to player profile
|
||||
const cultistProduction = this.hideoutHelper.initProduction(
|
||||
recipeId,
|
||||
this.getCircleCraftTimeSeconds(rewardAmountRoubles, directRewardSettings),
|
||||
false,
|
||||
);
|
||||
const cultistProduction = this.hideoutHelper.initProduction(recipeId, craftingTime, false);
|
||||
|
||||
// Flag as cultist circle for code to pick up later
|
||||
cultistProduction.sptIsCultistCircle = true;
|
||||
@ -196,35 +228,79 @@ export class CircleOfCultistService {
|
||||
|
||||
/**
|
||||
* Get the circle craft time as seconds, value is based on reward item value
|
||||
* OR rewards are direct, then use custom craft time defined in oarameter object
|
||||
* And get the bonus status to determine what tier of reward is given
|
||||
* @param rewardAmountRoubles Value of rewards in roubles
|
||||
* @param directRewardSettings OPTIONAL: If craft is giving direct rewards
|
||||
* @returns craft time seconds
|
||||
* @param circleConfig Circle config values
|
||||
* @param directRewardSettings OPTIONAL - Values related to direct reward being given
|
||||
* @returns craft time + type of reward + reward details
|
||||
*/
|
||||
protected getCircleCraftTimeSeconds(
|
||||
protected getCircleCraftingInfo(
|
||||
rewardAmountRoubles: number,
|
||||
circleConfig: ICultistCircleSettings,
|
||||
directRewardSettings?: IDirectRewardSettings,
|
||||
): number {
|
||||
// Edge case, check if override exists
|
||||
if (this.hideoutConfig.cultistCircle.craftTimeOverride !== -1) {
|
||||
return this.hideoutConfig.cultistCircle.craftTimeOverride;
|
||||
}
|
||||
): ICraftDetails {
|
||||
const result = {
|
||||
time: -1,
|
||||
rewardType: CircleRewardType.RANDOM,
|
||||
rewardAmountRoubles: rewardAmountRoubles,
|
||||
rewardDetails: null,
|
||||
};
|
||||
|
||||
// Craft is rewarding items directly, use custom craft time
|
||||
// Direct reward edge case
|
||||
if (directRewardSettings) {
|
||||
return directRewardSettings.craftTimeSeconds;
|
||||
result.time = directRewardSettings.craftTimeSeconds;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const thresholds = this.hideoutConfig.cultistCircle.craftTimeThreshholds;
|
||||
// Get a threshold where sacrificed amount is between thresholds min and max
|
||||
const matchingThreshold = this.getMatchingThreshold(circleConfig.craftTimeThreshholds, rewardAmountRoubles);
|
||||
if (
|
||||
rewardAmountRoubles >= circleConfig.hideoutCraftSacrificeThresholdRub &&
|
||||
Math.random() <= circleConfig.bonusChanceMultiplier
|
||||
) {
|
||||
// Sacrifice amount is enough + passed 25% check to get hideout/task rewards
|
||||
result.time =
|
||||
circleConfig.craftTimeOverride !== -1
|
||||
? circleConfig.craftTimeOverride
|
||||
: circleConfig.hideoutTaskRewardTimeSeconds;
|
||||
result.rewardType = CircleRewardType.HIDEOUT_TASK;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Edge case, check if override exists, Otherwise use matching threshold craft time
|
||||
result.time =
|
||||
circleConfig.craftTimeOverride !== -1 ? circleConfig.craftTimeOverride : matchingThreshold.craftTimeSeconds;
|
||||
|
||||
result.rewardDetails = matchingThreshold;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected getMatchingThreshold(
|
||||
thresholds: ICraftTimeThreshhold[],
|
||||
rewardAmountRoubles: number,
|
||||
): ICraftTimeThreshhold {
|
||||
const matchingThreshold = thresholds.find(
|
||||
(craftThreshold) => craftThreshold.min <= rewardAmountRoubles && craftThreshold.max >= rewardAmountRoubles,
|
||||
);
|
||||
|
||||
// No matching threshold, make one
|
||||
if (!matchingThreshold) {
|
||||
// No craft time found, default to 12 hours
|
||||
return this.timeUtil.getHoursAsSeconds(12);
|
||||
// None found, use a defalt
|
||||
this.logger.warning("Unable to find a matching cultist circle threshold, using fallback of 12 hours");
|
||||
|
||||
// Use first threshold value (cheapest) from parameter array, otherwise use 12 hours
|
||||
const firstThreshold = thresholds[0];
|
||||
const craftTime = firstThreshold?.craftTimeSeconds
|
||||
? firstThreshold.craftTimeSeconds
|
||||
: this.timeUtil.getHoursAsSeconds(12);
|
||||
|
||||
return { min: firstThreshold?.min ?? 1, max: firstThreshold?.max ?? 34999, craftTimeSeconds: craftTime };
|
||||
}
|
||||
|
||||
return matchingThreshold.craftTimeSeconds;
|
||||
return matchingThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,6 +338,7 @@ export class CircleOfCultistService {
|
||||
rewardItemTplPool: string[],
|
||||
rewardBudget: number,
|
||||
cultistCircleStashId: string,
|
||||
circleConfig: ICultistCircleSettings,
|
||||
): IItem[][] {
|
||||
// Prep rewards array (reward can be item with children, hence array of arrays)
|
||||
const rewards: IItem[][] = [];
|
||||
@ -273,9 +350,9 @@ export class CircleOfCultistService {
|
||||
while (
|
||||
totalRewardCost < rewardBudget &&
|
||||
rewardItemTplPool.length > 0 &&
|
||||
rewardItemCount < this.hideoutConfig.cultistCircle.maxRewardItemCount
|
||||
rewardItemCount < circleConfig.maxRewardItemCount
|
||||
) {
|
||||
if (failedAttempts > this.hideoutConfig.cultistCircle.maxAttemptsToPickRewardsWithinBudget) {
|
||||
if (failedAttempts > circleConfig.maxAttemptsToPickRewardsWithinBudget) {
|
||||
this.logger.warning(`Exiting reward generation after ${failedAttempts} failed attempts`);
|
||||
|
||||
break;
|
||||
@ -340,46 +417,31 @@ export class CircleOfCultistService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Give every item as a reward that's passed in
|
||||
* @param rewardTpls Item tpls to turn into reward items
|
||||
* Get direct rewards
|
||||
* @param sessionId sessionId
|
||||
* @param directReward Items sacrificed
|
||||
* @param cultistCircleStashId Id of stash item
|
||||
* @returns Array of item arrays
|
||||
* @returns The reward object
|
||||
*/
|
||||
protected getExplicitRewards(
|
||||
explicitRewardSettings: IDirectRewardSettings,
|
||||
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[][] = [];
|
||||
for (const rewardTpl of explicitRewardSettings.rewardTpls) {
|
||||
if (
|
||||
this.itemHelper.armorItemHasRemovableOrSoftInsertSlots(rewardTpl) ||
|
||||
this.itemHelper.isOfBaseclass(rewardTpl, BaseClasses.WEAPON)
|
||||
) {
|
||||
const defaultPreset = this.presetHelper.getDefaultPreset(rewardTpl);
|
||||
if (!defaultPreset) {
|
||||
this.logger.warning(`Reward tpl: ${rewardTpl} lacks a default preset, skipping reward`);
|
||||
|
||||
continue;
|
||||
}
|
||||
// Handle special case of tagilla helmets - only one reward is allowed
|
||||
if (directReward.reward.includes(ItemTpl.FACECOVER_TAGILLAS_WELDING_MASK_GORILLA)) {
|
||||
directReward.reward = [this.randomUtil.getArrayValue(directReward.reward)];
|
||||
}
|
||||
|
||||
// Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory
|
||||
const presetAndMods: IItem[] = this.itemHelper.replaceIDs(defaultPreset._items);
|
||||
|
||||
this.itemHelper.remapRootItemId(presetAndMods);
|
||||
|
||||
rewards.push(presetAndMods);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some items can have variable stack size, e.g. ammo
|
||||
const stackSize = this.getExplicitRewardBaseTypeStackSize(rewardTpl);
|
||||
|
||||
// Not a weapon/armor, standard single item
|
||||
// Loop because these can include multiple rewards
|
||||
for (const reward of directReward.reward) {
|
||||
const stackSize = this.getDirectRewardBaseTypeStackSize(reward);
|
||||
const rewardItem: IItem = {
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: rewardTpl,
|
||||
_tpl: reward,
|
||||
parentId: cultistCircleStashId,
|
||||
slotId: CircleOfCultistService.circleOfCultistSlotId,
|
||||
upd: {
|
||||
@ -387,19 +449,67 @@ export class CircleOfCultistService {
|
||||
SpawnedInSession: true,
|
||||
},
|
||||
};
|
||||
|
||||
rewards.push([rewardItem]);
|
||||
}
|
||||
// Direct reward is not repeatable, flag collected in profile
|
||||
if (!directReward.repeatable) {
|
||||
this.flagDirectRewardAsAcceptedInProfile(sessionId, directReward);
|
||||
}
|
||||
|
||||
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(
|
||||
sessionId: string,
|
||||
sacrificedItems: IItem[],
|
||||
directRewardsCache: Map<string, IDirectRewardSettings>,
|
||||
): IDirectRewardSettings {
|
||||
// Get sacrificed tpls
|
||||
const sacrificedItemTpls = sacrificedItems.map((item) => item._tpl);
|
||||
|
||||
// Create md5 key of the items player sacrificed so we can compare against the direct reward cache
|
||||
const sacrificedItemsKey = this.hashUtil.generateMd5ForData(sacrificedItemTpls.sort().join(","));
|
||||
|
||||
const matchingDirectReward = directRewardsCache.get(sacrificedItemsKey);
|
||||
if (!matchingDirectReward) {
|
||||
// No direct reward
|
||||
return null;
|
||||
}
|
||||
|
||||
const fullProfile = this.profileHelper.getFullProfile(sessionId);
|
||||
const directRewardHash = this.getDirectRewardHashKey(matchingDirectReward);
|
||||
if (fullProfile.spt.cultistRewards?.has(directRewardHash)) {
|
||||
// Player has already received this direct reward
|
||||
return null;
|
||||
}
|
||||
|
||||
return matchingDirectReward;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an md5 key of the sacrificed + reward items
|
||||
* @param directReward Direct reward to create key for
|
||||
* @returns Key
|
||||
*/
|
||||
protected getDirectRewardHashKey(directReward: IDirectRewardSettings): string {
|
||||
// Key is sacrificed items separated by commas, a dash, then the rewards separated by commas
|
||||
const key = `{${directReward.requiredItems.sort().join(",")}-${directReward.reward.sort().join(",")}`;
|
||||
|
||||
return this.hashUtil.generateMd5ForData(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit rewards have thier own stack sizes as they dont use a reward rouble pool
|
||||
* @param rewardTpl Item being rewarded to get stack size of
|
||||
* @returns stack size of item
|
||||
*/
|
||||
protected getExplicitRewardBaseTypeStackSize(rewardTpl: string) {
|
||||
protected getDirectRewardBaseTypeStackSize(rewardTpl: string): number {
|
||||
const itemDetails = this.itemHelper.getItem(rewardTpl);
|
||||
if (!itemDetails[0]) {
|
||||
this.logger.warning(`${rewardTpl} is not an item, setting stack size to 1`);
|
||||
@ -416,6 +526,22 @@ export class CircleOfCultistService {
|
||||
return this.randomUtil.getInt(settings.min, settings.max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a record to the players profile to signal they have accepted a non-repeatable direct reward
|
||||
* @param sessionId Session id
|
||||
* @param directReward Reward sent to player
|
||||
*/
|
||||
protected flagDirectRewardAsAcceptedInProfile(sessionId: string, directReward: IDirectRewardSettings) {
|
||||
const fullProfile = this.profileHelper.getFullProfile(sessionId);
|
||||
const dataToStoreInProfile: IAcceptedCultistReward = {
|
||||
timestamp: this.timeUtil.getTimestamp(),
|
||||
sacrificeItems: directReward.requiredItems,
|
||||
rewardItems: directReward.reward,
|
||||
};
|
||||
|
||||
fullProfile.spt.cultistRewards.set(this.getDirectRewardHashKey(directReward), dataToStoreInProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a reward items stack
|
||||
* 1 for everything except ammo, ammo can be between min stack and max stack
|
||||
@ -455,72 +581,87 @@ export class CircleOfCultistService {
|
||||
* Get a pool of tpl IDs of items the player needs to complete hideout crafts/upgrade areas
|
||||
* @param sessionId Session id
|
||||
* @param pmcData Profile of player who will be getting the rewards
|
||||
* @param rewardType Do we return bonus items (hideout/task items)
|
||||
* @param cultistCircleConfig Circle config
|
||||
* @returns Array of tpls
|
||||
*/
|
||||
protected getCultistCircleRewardPool(sessionId: string, pmcData: IPmcData): string[] {
|
||||
protected getCultistCircleRewardPool(
|
||||
sessionId: string,
|
||||
pmcData: IPmcData,
|
||||
craftingInfo: ICraftDetails,
|
||||
cultistCircleConfig: ICultistCircleSettings,
|
||||
): string[] {
|
||||
const rewardPool = new Set<string>();
|
||||
const cultistCircleConfig = this.hideoutConfig.cultistCircle;
|
||||
const hideoutDbData = this.databaseService.getHideout();
|
||||
|
||||
// Merge reward item blacklist with cultist circle blacklist from config
|
||||
// Merge reward item blacklist and boss item blacklist with cultist circle blacklist from config
|
||||
const itemRewardBlacklist = [
|
||||
...this.seasonalEventService.getInactiveSeasonalEventItems(),
|
||||
...this.itemFilterService.getItemRewardBlacklist(),
|
||||
...cultistCircleConfig.rewardItemBlacklist,
|
||||
];
|
||||
|
||||
// What does player need to upgrade hideout areas
|
||||
const dbAreas = hideoutDbData.areas;
|
||||
for (const area of this.getPlayerAccessibleHideoutAreas(pmcData.Hideout.Areas)) {
|
||||
const currentStageLevel = area.level;
|
||||
const areaType = area.type;
|
||||
// Hideout and task rewards are ONLY if the bonus is active
|
||||
switch (craftingInfo.rewardType) {
|
||||
case CircleRewardType.RANDOM: {
|
||||
// Just random items so we'll add maxRewardItemCount * 2 amount of random things
|
||||
|
||||
// Get next stage of area
|
||||
const dbArea = dbAreas.find((area) => area.type === areaType);
|
||||
const nextStageDbData = dbArea.stages[currentStageLevel + 1];
|
||||
if (nextStageDbData) {
|
||||
// Next stage exists, gather up requirements and add to pool
|
||||
const itemRequirements = this.getItemRequirements(nextStageDbData.requirements);
|
||||
for (const rewardToAdd of itemRequirements) {
|
||||
if (itemRewardBlacklist.includes(rewardToAdd.templateId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rewardPool.add(rewardToAdd.templateId);
|
||||
}
|
||||
// Does reward pass the high value threshold
|
||||
const isHighValueReward = craftingInfo.rewardAmountRoubles >= cultistCircleConfig.highValueThresholdRub;
|
||||
this.getRandomLoot(rewardPool, itemRewardBlacklist, isHighValueReward);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// What does player need to start crafts with
|
||||
const playerUnlockedRecipes = pmcData.UnlockedInfo?.unlockedProductionRecipe ?? [];
|
||||
const allRecipes = hideoutDbData.production;
|
||||
for (const recipe of this.getPlayerAccessibleRecipes(playerUnlockedRecipes, allRecipes)) {
|
||||
const itemRequirements = this.getItemRequirements(recipe.requirements);
|
||||
for (const requirement of itemRequirements) {
|
||||
if (itemRewardBlacklist.includes(requirement.templateId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rewardPool.add(requirement.templateId);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for scav case unlock in profile
|
||||
const hasScavCaseAreaUnlocked = pmcData.Hideout.Areas[HideoutAreas.SCAV_CASE]?.level > 0;
|
||||
if (hasScavCaseAreaUnlocked) {
|
||||
// Gather up items used to start scav case crafts
|
||||
const scavCaseCrafts = hideoutDbData.production.scavRecipes;
|
||||
for (const craft of scavCaseCrafts) {
|
||||
// Find the item requirements from each craft
|
||||
const itemRequirements = this.getItemRequirements(craft.requirements);
|
||||
for (const requirement of itemRequirements) {
|
||||
if (itemRewardBlacklist.includes(requirement.templateId)) {
|
||||
continue;
|
||||
case CircleRewardType.HIDEOUT_TASK: {
|
||||
// Hideout/Task loot
|
||||
// Add hideout upgrade requirements
|
||||
const dbAreas = hideoutDbData.areas;
|
||||
for (const area of this.getPlayerAccessibleHideoutAreas(pmcData.Hideout.Areas)) {
|
||||
const currentStageLevel = area.level;
|
||||
const areaType = area.type;
|
||||
// Get next stage of area
|
||||
const dbArea = dbAreas.find((area) => area.type === areaType);
|
||||
const nextStageDbData = dbArea.stages[currentStageLevel + 1];
|
||||
if (nextStageDbData) {
|
||||
// Next stage exists, gather up requirements and add to pool
|
||||
const itemRequirements = this.getItemRequirements(nextStageDbData.requirements);
|
||||
for (const rewardToAdd of itemRequirements) {
|
||||
if (
|
||||
itemRewardBlacklist.includes(rewardToAdd.templateId) ||
|
||||
!this.itemHelper.isValidItem(rewardToAdd.templateId)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
this.logger.debug(
|
||||
`Added Hideout Loot: ${this.itemHelper.getItemName(rewardToAdd.templateId)}`,
|
||||
);
|
||||
rewardPool.add(rewardToAdd.templateId);
|
||||
}
|
||||
}
|
||||
|
||||
// Add tpl to reward pool
|
||||
rewardPool.add(requirement.templateId);
|
||||
}
|
||||
|
||||
// Add task/quest items
|
||||
const activeTasks = pmcData.Quests.filter((quest) => quest.status === QuestStatus.Started);
|
||||
for (const task of activeTasks) {
|
||||
const questData = this.questHelper.getQuestFromDb(task.qid, pmcData);
|
||||
const handoverConditions = questData.conditions.AvailableForFinish.filter(
|
||||
(c) => c.conditionType === "HandoverItem",
|
||||
);
|
||||
for (const condition of handoverConditions) {
|
||||
for (const neededItem of condition.target) {
|
||||
if (itemRewardBlacklist.includes(neededItem) || !this.itemHelper.isValidItem(neededItem)) {
|
||||
continue;
|
||||
}
|
||||
this.logger.debug(`Added Task Loot: ${this.itemHelper.getItemName(neededItem)}`);
|
||||
rewardPool.add(neededItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no tasks or hideout stuff left or need more loot to fill it out, default to high value
|
||||
if (rewardPool.size < cultistCircleConfig.maxRewardItemCount + 2) {
|
||||
this.getRandomLoot(rewardPool, itemRewardBlacklist, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,17 +697,45 @@ export class CircleOfCultistService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all recipes the player has access to, includes base + unlocked recipes
|
||||
* @param unlockedRecipes Recipes player has flagged as unlocked
|
||||
* @param allRecipes All recipes
|
||||
* @returns Array of recipes
|
||||
* Get array of random reward items
|
||||
* @param rewardPool Reward pool to add to
|
||||
* @param itemRewardBlacklist Reward Blacklist
|
||||
* @param valuable Should these items meet the valuable threshold
|
||||
* @returns rewardPool
|
||||
*/
|
||||
protected getPlayerAccessibleRecipes(
|
||||
unlockedRecipes: string[],
|
||||
allRecipes: IHideoutProductionData,
|
||||
): IHideoutProduction[] {
|
||||
// Get default unlocked recipes + locked recipes they've unlocked
|
||||
return allRecipes.recipes.filter((recipe) => !recipe.locked || unlockedRecipes.includes(recipe._id));
|
||||
protected getRandomLoot(rewardPool: Set<string>, itemRewardBlacklist: string[], valuable: boolean): Set<string> {
|
||||
const allItems = this.itemHelper.getItems();
|
||||
let currentItemCount = 0;
|
||||
let attempts = 0;
|
||||
// currentItemCount var will look for the correct number of items, attempts var will keep this from never stopping if the highValueThreshold is too high
|
||||
while (
|
||||
currentItemCount < this.hideoutConfig.cultistCircle.maxRewardItemCount + 2 &&
|
||||
attempts < allItems.length
|
||||
) {
|
||||
attempts++;
|
||||
const randomItem = this.randomUtil.getArrayValue(allItems);
|
||||
if (
|
||||
itemRewardBlacklist.includes(randomItem._id) ||
|
||||
BaseClasses.AMMO === randomItem._parent ||
|
||||
BaseClasses.MONEY === randomItem._parent ||
|
||||
!this.itemHelper.isValidItem(randomItem._id)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Valuable check
|
||||
if (valuable) {
|
||||
const itemValue = this.itemHelper.getItemMaxPrice(randomItem._id);
|
||||
if (itemValue < this.hideoutConfig.cultistCircle.highValueThresholdRub) {
|
||||
this.logger.debug(`Ignored due to value: ${this.itemHelper.getItemName(randomItem._id)}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this.logger.debug(`Added: ${this.itemHelper.getItemName(randomItem._id)}`);
|
||||
rewardPool.add(randomItem._id);
|
||||
currentItemCount++;
|
||||
}
|
||||
return rewardPool;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -578,3 +747,15 @@ export class CircleOfCultistService {
|
||||
return requirements.filter((requirement) => requirement.type === "Item");
|
||||
}
|
||||
}
|
||||
|
||||
export enum CircleRewardType {
|
||||
RANDOM = 0,
|
||||
HIDEOUT_TASK = 1,
|
||||
}
|
||||
|
||||
export interface ICraftDetails {
|
||||
time: number;
|
||||
rewardType: CircleRewardType;
|
||||
rewardAmountRoubles: number;
|
||||
rewardDetails?: ICraftTimeThreshhold;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user