Skip to content

Commit

Permalink
Additional Cultist Circle Stuff (#971)
Browse files Browse the repository at this point in the history
@RuKira provided a bunch of info regarding the circle that wasn't
known/implemented. Some items are not repeatable, tagilla helms are OR
not AND, and Tallan gave me bad info on boss loot.

All fixed in this, although it did involve touching the profile which is
not something I've messed with before but I couldn't think of an easier
way to track non-repeatable swaps.
  • Loading branch information
chompDev authored Dec 5, 2024
2 parents b73bcb6 + ff3a985 commit 3b4a439
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 24 deletions.
49 changes: 30 additions & 19 deletions project/assets/configs/hideout.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
5 changes: 5 additions & 0 deletions project/src/controllers/GameController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions project/src/models/eft/profile/ISptProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ 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 */
cultistRewards?: string[][];
}

export interface IModDetails {
Expand Down
1 change: 1 addition & 0 deletions project/src/models/spt/config/IHideoutConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ export interface IDirectRewardSettings {
reward: string[];
requiredItems: string[];
craftTimeSeconds: number;
repeatable: boolean;
}
29 changes: 24 additions & 5 deletions project/src/services/CircleOfCultistService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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)) {
Expand Down Expand Up @@ -476,7 +496,6 @@ export class CircleOfCultistService {
const itemRewardBlacklist = [
...this.seasonalEventService.getInactiveSeasonalEventItems(),
...this.itemFilterService.getItemRewardBlacklist(),
...this.itemFilterService.getBossItems(),
...cultistCircleConfig.rewardItemBlacklist,
];

Expand Down

0 comments on commit 3b4a439

Please sign in to comment.