Expanded direct rewards to have a custom craft time

Migrated direct reward stack count system to use items parent id instead of tpl
This commit is contained in:
Dev 2024-09-13 11:27:42 +01:00
parent a578e01402
commit a380db2995
3 changed files with 77 additions and 29 deletions

View File

@ -29,23 +29,23 @@
],
"craftTimeOverride": 40,
"directRewards": {
"66572c82ad599021091c6118": ["5c0e874186f7745dc7616606"],
"66572be36a723f7f005a066e": ["5b3b713c5acfc4330140bd8d"],
"655c669103999d3c810c025b": ["635267ab3c89e2112001f826"],
"66572cbdad599021091c611a": ["60a7ad2a2198820d95707a2e"],
"5c0530ee86f774697952d952": ["6389c8c5dbfd5e4b95197e6b"],
"66572b8d80b1cd4b6a67847f": ["5bc9b9ecd4351e3bac122519", "62a09dd4621468534a797ac7"],
"5c093ca986f7740a1867ab12": ["5732ee6a24597719ae0c0281"],
"665ee77ccf2d642e98220bca": ["5857a8bc2459772bad15db29"],
"59faff1d86f7746c51718c9c": [
"66572c82ad599021091c6118": {"rewardTpls": ["5c0e874186f7745dc7616606"], "craftTimeSeconds": 100},
"66572be36a723f7f005a066e": {"rewardTpls": ["5b3b713c5acfc4330140bd8d"], "craftTimeSeconds": 100},
"655c669103999d3c810c025b": {"rewardTpls": ["635267ab3c89e2112001f826"], "craftTimeSeconds": 100},
"66572cbdad599021091c611a": {"rewardTpls": ["60a7ad2a2198820d95707a2e"], "craftTimeSeconds": 100},
"5c0530ee86f774697952d952": {"rewardTpls": ["6389c8c5dbfd5e4b95197e6b"], "craftTimeSeconds": 100},
"66572b8d80b1cd4b6a67847f": {"rewardTpls": ["5bc9b9ecd4351e3bac122519", "62a09dd4621468534a797ac7"], "craftTimeSeconds": 100},
"5c093ca986f7740a1867ab12": {"rewardTpls": ["5732ee6a24597719ae0c0281"], "craftTimeSeconds": 100},
"665ee77ccf2d642e98220bca": {"rewardTpls": ["5857a8bc2459772bad15db29"], "craftTimeSeconds": 100},
"59faff1d86f7746c51718c9c": {"rewardTpls": [
"5c12620d86f7743f8b198b72",
"5c12620d86f7743f8b198b72",
"5e2aedd986f7746d404f3aa4",
"5e2aedd986f7746d404f3aa4"
]
], "craftTimeSeconds": 100}
},
"directRewardStackSize": {
"exampleTpl": {"min": 1000, "max": 50000}
"exampleParentId": {"min": 1000, "max": 50000}
},
"rewardItemBlacklist": [],
"additionalRewardItemPool": [],

View File

@ -24,7 +24,7 @@ export interface ICultistCircleSettings {
/** -1 means no override */
craftTimeOverride: number;
/** Specific reward pool when player sacrificed one specific item */
directRewards: Record<string, string[]>;
directRewards: Record<string, DirectRewardSettings>;
directRewardStackSize: Record<string, MinMax>;
/** Item tpls to exclude from the reward pool */
rewardItemBlacklist: string[];
@ -34,5 +34,10 @@ export interface ICultistCircleSettings {
}
export interface CraftTimeThreshhold extends MinMax {
timeSeconds: number;
craftTimeSeconds: number;
}
export interface DirectRewardSettings {
rewardTpls: string[];
craftTimeSeconds: number;
}

View File

@ -20,7 +20,7 @@ import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { HideoutAreas } from "@spt/models/enums/HideoutAreas";
import { ItemTpl } from "@spt/models/enums/ItemTpl";
import { SkillTypes } from "@spt/models/enums/SkillTypes";
import { IHideoutConfig } from "@spt/models/spt/config/IHideoutConfig";
import { DirectRewardSettings, 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";
@ -97,6 +97,10 @@ export class CircleOfCultistService {
// Get the rouble amount we generate rewards with from cost of sacrified items * above multipler
const rewardAmountRoubles = 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;
// Create production in pmc profile
this.registerCircleOfCultistProduction(
sessionId,
@ -104,6 +108,7 @@ export class CircleOfCultistService {
cultistCraftData._id,
sacrificedItems,
rewardAmountRoubles,
directRewardSettings,
);
const output = this.eventOutputHolder.getOutput(sessionId);
@ -115,13 +120,9 @@ export class CircleOfCultistService {
}
}
// Player has sacrified a single item that's in directReward dict, return specific reward pool
let rewards: Item[][];
if (sacrificedItems.length === 1 && this.hideoutConfig.cultistCircle.directRewards[sacrificedItems[0]._tpl]) {
rewards = this.getExplicitRewards(
this.hideoutConfig.cultistCircle.directRewards[sacrificedItems[0]._tpl],
cultistCircleStashId,
);
if (hasSacrificedSingleItemFlaggedInConfig) {
rewards = this.getExplicitRewards(directRewardSettings, cultistCircleStashId);
} else {
const rewardItemPool = this.getCultistCircleRewardPool(sessionId, pmcData);
rewards = this.getRewardsWithinBudget(rewardItemPool, rewardAmountRoubles, cultistCircleStashId);
@ -159,36 +160,59 @@ export class CircleOfCultistService {
return output;
}
/**
* 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
*/
protected registerCircleOfCultistProduction(
sessionId: string,
pmcData: IPmcData,
recipeId: string,
sacrificedItems: Item[],
rewardAmountRoubles: number,
directRewardSettings?: DirectRewardSettings,
): void {
// Create circle production/craft object to add to player profile
const cultistProduction = this.hideoutHelper.initProduction(
recipeId,
this.getCircleCraftTime(rewardAmountRoubles),
this.getCircleCraftTimeSeconds(rewardAmountRoubles, directRewardSettings),
false,
true,
);
// Add items player sacrificed
cultistProduction.GivenItemsInStart = sacrificedItems;
// Add circle production to profile
// Add circle production to profile keyed to recipe id
pmcData.Hideout.Production[recipeId] = cultistProduction;
}
/**
* 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
* @param rewardAmountRoubles Value of rewards in roubles
* @param directRewardSettings OPTIONAL: If craft is giving direct rewards
* @returns craft time seconds
*/
protected getCircleCraftTime(rewardAmountRoubles: number): number {
protected getCircleCraftTimeSeconds(
rewardAmountRoubles: number,
directRewardSettings?: DirectRewardSettings,
): number {
// Edge case, check if override exists
if (this.hideoutConfig.cultistCircle.craftTimeOverride !== -1) {
return this.hideoutConfig.cultistCircle.craftTimeOverride;
}
// Craft is rewarding items directly, use custom craft time
if (directRewardSettings) {
return directRewardSettings.craftTimeSeconds;
}
const thresholds = this.hideoutConfig.cultistCircle.craftTimeThreshholds;
const matchingThreshold = thresholds.find(
(craftThreshold) => craftThreshold.min <= rewardAmountRoubles && craftThreshold.max >= rewardAmountRoubles,
@ -198,7 +222,7 @@ export class CircleOfCultistService {
return this.timeUtil.getHoursAsSeconds(12);
}
return matchingThreshold.timeSeconds;
return matchingThreshold.craftTimeSeconds;
}
/**
@ -319,10 +343,10 @@ export class CircleOfCultistService {
* @param cultistCircleStashId Id of stash item
* @returns Array of item arrays
*/
protected getExplicitRewards(rewardTpls: string[], cultistCircleStashId: string): Item[][] {
protected getExplicitRewards(explicitRewardSettings: DirectRewardSettings, cultistCircleStashId: string): Item[][] {
// Prep rewards array (reward can be item with children, hence array of arrays)
const rewards: Item[][] = [];
for (const rewardTpl of rewardTpls) {
for (const rewardTpl of explicitRewardSettings.rewardTpls) {
if (
this.itemHelper.armorItemHasRemovableOrSoftInsertSlots(rewardTpl) ||
this.itemHelper.isOfBaseclass(rewardTpl, BaseClasses.WEAPON)
@ -345,7 +369,7 @@ export class CircleOfCultistService {
}
// Some items can have variable stack size, e.g. ammo
const stackSize = this.getExplicitRewardStackSize(rewardTpl);
const stackSize = this.getExplicitRewardBaseTypeStackSize(rewardTpl);
// Not a weapon/armor, standard single item
const rewardItem: Item = {
@ -365,8 +389,21 @@ export class CircleOfCultistService {
return rewards;
}
protected getExplicitRewardStackSize(rewardTpl: string) {
const settings = this.hideoutConfig.cultistCircle.directRewardStackSize[rewardTpl];
/**
* 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) {
const itemDetails = this.itemHelper.getItem(rewardTpl);
if (!itemDetails[0]) {
this.logger.warning(`${rewardTpl} is not an item, setting stack size to 1`);
return 1;
}
// Look for parent in dict
const settings = this.hideoutConfig.cultistCircle.directRewardStackSize[itemDetails[1]._parent];
if (!settings) {
return 1;
}
@ -512,6 +549,12 @@ 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
*/
protected getPlayerAccessibleRecipes(
unlockedRecipes: string[],
allRecipes: IHideoutProductionData,