From 8a9b106a5590292e023961e334d953b69c6f55e5 Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 5 Jan 2025 11:59:31 +0100 Subject: [PATCH 01/10] feat: activate riven mod (get a random challenge) --- package-lock.json | 8 +- package.json | 2 +- .../api/activateRandomModController.ts | 98 +++++++++++++++++++ src/routes/api.ts | 2 + src/services/rngService.ts | 12 +++ 5 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 src/controllers/api/activateRandomModController.ts diff --git a/package-lock.json b/package-lock.json index 95bfdf39..09f0c344 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "copyfiles": "^2.4.1", "express": "^5", "mongoose": "^8.9.2", - "warframe-public-export-plus": "^0.5.19", + "warframe-public-export-plus": "^0.5.21", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" @@ -3778,9 +3778,9 @@ } }, "node_modules/warframe-public-export-plus": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.19.tgz", - "integrity": "sha512-ERCPAe4ojJXts6tyNPBvNsFcgAwJuV3M04iDfXhudJfpJrg0qseDO4AExjSyFo+WUvKoWROMCy9dCRzxIbNATw==" + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.21.tgz", + "integrity": "sha512-06k63L99wfX+lPx7ReYzGiMK/7NtNEiO97r+kemrtn4QIEKCfvBvmKiJcYbkSo79x35CQ+6FQfMtDilf6DGz6Q==" }, "node_modules/warframe-riven-info": { "version": "0.1.2", diff --git a/package.json b/package.json index 4f789119..b241a343 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "copyfiles": "^2.4.1", "express": "^5", "mongoose": "^8.9.2", - "warframe-public-export-plus": "^0.5.19", + "warframe-public-export-plus": "^0.5.21", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" diff --git a/src/controllers/api/activateRandomModController.ts b/src/controllers/api/activateRandomModController.ts new file mode 100644 index 00000000..a0ef4745 --- /dev/null +++ b/src/controllers/api/activateRandomModController.ts @@ -0,0 +1,98 @@ +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { addMods, getInventory } from "@/src/services/inventoryService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { getRandomElement, getRandomInt, getRandomReward, IRngResult } from "@/src/services/rngService"; +import { logger } from "@/src/utils/logger"; +import { RequestHandler } from "express"; +import { ExportUpgrades } from "warframe-public-export-plus"; + +export const activateRandomModController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const request = getJSONfromString(String(req.body)) as IActiveRandomModRequest; + addMods(inventory, [ + { + ItemType: request.ItemType, + ItemCount: -1 + } + ]); + const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType]); + const challenge = getRandomElement(ExportUpgrades[rivenType].availableChallenges!); + const fingerprintChallenge: IRandomModChallenge = { + Type: challenge.fullName, + Progress: 0, + Required: getRandomInt(challenge.countRange[0], challenge.countRange[1]) + }; + if (Math.random() < challenge.complicationChance) { + const complicationsAsRngResults: IRngResult[] = []; + for (const complication of challenge.complications) { + complicationsAsRngResults.push({ + type: complication.fullName, + itemCount: 1, + probability: complication.weight + }); + } + fingerprintChallenge.Complication = getRandomReward(complicationsAsRngResults)!.type; + logger.debug( + `riven rolled challenge ${fingerprintChallenge.Type} with complication ${fingerprintChallenge.Complication}` + ); + const complication = challenge.complications.find(x => x.fullName == fingerprintChallenge.Complication)!; + fingerprintChallenge.Required *= complication.countMultiplier; + } else { + logger.debug(`riven rolled challenge ${fingerprintChallenge.Type}`); + } + const upgradeIndex = + inventory.Upgrades.push({ + ItemType: rivenType, + UpgradeFingerprint: JSON.stringify({ challenge: fingerprintChallenge }) + }) - 1; + await inventory.save(); + res.json({ + NewMod: inventory.Upgrades[upgradeIndex].toJSON() + }); +}; + +interface IActiveRandomModRequest { + ItemType: string; +} + +interface IRandomModChallenge { + Type: string; + Progress: number; + Required: number; + Complication?: string; +} + +const rivenRawToRealWeighted: Record = { + "/Lotus/Upgrades/Mods/Randomized/RawArchgunRandomMod": [ + "/Lotus/Upgrades/Mods/Randomized/LotusArchgunRandomModRare" + ], + "/Lotus/Upgrades/Mods/Randomized/RawMeleeRandomMod": [ + "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare" + ], + "/Lotus/Upgrades/Mods/Randomized/RawModularMeleeRandomMod": [ + "/Lotus/Upgrades/Mods/Randomized/LotusModularMeleeRandomModRare" + ], + "/Lotus/Upgrades/Mods/Randomized/RawModularPistolRandomMod": [ + "/Lotus/Upgrades/Mods/Randomized/LotusModularPistolRandomModRare" + ], + "/Lotus/Upgrades/Mods/Randomized/RawPistolRandomMod": ["/Lotus/Upgrades/Mods/Randomized/LotusPistolRandomModRare"], + "/Lotus/Upgrades/Mods/Randomized/RawRifleRandomMod": ["/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare"], + "/Lotus/Upgrades/Mods/Randomized/RawShotgunRandomMod": [ + "/Lotus/Upgrades/Mods/Randomized/LotusShotgunRandomModRare" + ], + "/Lotus/Upgrades/Mods/Randomized/RawSentinelWeaponRandomMod": [ + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusRifleRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusShotgunRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/LotusPistolRandomModRare", + "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare" + ] +}; diff --git a/src/routes/api.ts b/src/routes/api.ts index 4194f44c..bdefaac8 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -1,4 +1,5 @@ import express from "express"; +import { activateRandomModController } from "@/src/controllers/api/activateRandomModController"; import { addFriendImageController } from "@/src/controllers/api/addFriendImageController"; import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController"; import { artifactsController } from "../controllers/api/artifactsController"; @@ -107,6 +108,7 @@ apiRouter.get("/surveys.php", surveysController); apiRouter.get("/updateSession.php", updateSessionGetController); // post +apiRouter.post("/activateRandomMod.php", activateRandomModController); apiRouter.post("/addFriendImage.php", addFriendImageController); apiRouter.post("/arcaneCommon.php", arcaneCommonController); apiRouter.post("/artifacts.php", artifactsController); diff --git a/src/services/rngService.ts b/src/services/rngService.ts index fad3a4ff..df235860 100644 --- a/src/services/rngService.ts +++ b/src/services/rngService.ts @@ -6,6 +6,18 @@ export interface IRngResult { probability: number; } +export const getRandomElement = (arr: T[]): T => { + return arr[Math.floor(Math.random() * arr.length)]; +}; + +// Returns a random integer between min (inclusive) and max (inclusive). +// https://stackoverflow.com/a/1527820 +export const getRandomInt = (min: number, max: number): number => { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +}; + export const getRandomReward = (pool: IRngResult[]): IRngResult | undefined => { if (pool.length == 0) return; -- 2.47.2 From 8154f9bc364f125fcfd3b5354d160f522ac2a6d2 Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 5 Jan 2025 12:26:26 +0100 Subject: [PATCH 02/10] feat(webui): add "Fully Level Up Helminth" (#717) --- src/controllers/api/infestedFoundryController.ts | 16 ++++++++++++++++ static/webui/index.html | 3 ++- static/webui/script.js | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index 4fa5f694..0f0833d6 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -223,6 +223,22 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { break; } + case "custom_unlockall": { + const inventory = await getInventory(accountId); + inventory.InfestedFoundry ??= {}; + inventory.InfestedFoundry.XP ??= 0; + if (151875_00 > inventory.InfestedFoundry.XP) { + const recipeChanges = addInfestedFoundryXP( + inventory.InfestedFoundry, + 151875_00 - inventory.InfestedFoundry.XP + ); + addRecipes(inventory, recipeChanges); + await inventory.save(); + } + res.end(); + break; + } + default: throw new Error(`unhandled infestedFoundry mode: ${String(req.query.mode)}`); } diff --git a/static/webui/index.html b/static/webui/index.html index af629d1f..e5c4eca8 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -278,7 +278,8 @@
Account
- +

+
diff --git a/static/webui/script.js b/static/webui/script.js index 160ccfae..fd7fdf15 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -955,6 +955,12 @@ function unlockFocusSchool(upgradeType) { }); } +function doHelminthUnlockAll() { + revalidateAuthz(() => { + $.post("/api/infestedFoundry.php?" + window.authz + "&mode=custom_unlockall"); + }); +} + // Powersuit Route single.getRoute("#powersuit-route").on("beforeload", function () { -- 2.47.2 From 1bab76f58b9a0cf8cd37e36ac37c24bccb33eb8a Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 5 Jan 2025 12:37:08 +0100 Subject: [PATCH 03/10] fix: unlockAllScans not fully working with blacklisted enemies (#723) --- static/fixed_responses/allScans.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/static/fixed_responses/allScans.json b/static/fixed_responses/allScans.json index 8f186d29..938f7dd8 100644 --- a/static/fixed_responses/allScans.json +++ b/static/fixed_responses/allScans.json @@ -4346,5 +4346,17 @@ { "scans": 9999, "type": "/Lotus/Weapons/Infested/Melee/InfBoomerang/InfBoomerangSpawnAvatar" + }, + { + "scans": 9999, + "type": "/Lotus/Types/Game/CrewShip/GrineerDestroyer/DeepSpace/GrineerDSDestroyerAvatar" + }, + { + "scans": 9999, + "type": "/Lotus/Types/Game/CrewShip/GrineerDestroyer/Saturn/GrineerSaturnDestroyerAvatar" + }, + { + "scans": 9999, + "type": "/Lotus/Types/Game/CrewShip/GrineerDestroyer/GrineerDestroyerAvatar" } ] -- 2.47.2 From d69ebf89ec362dd4d32d79b9e5ea1fae5518262e Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 5 Jan 2025 13:34:41 +0100 Subject: [PATCH 04/10] feat: helminth losing apetite (#718) --- .../api/infestedFoundryController.ts | 78 ++++++++++++++++--- src/models/inventoryModels/inventoryModel.ts | 20 ++++- src/types/inventoryTypes/inventoryTypes.ts | 10 ++- 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index 0f0833d6..d52c7f74 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -8,6 +8,7 @@ import { ExportMisc, ExportRecipes } from "warframe-public-export-plus"; import { getRecipe } from "@/src/services/itemDataService"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { toMongoDate } from "@/src/helpers/inventoryHelpers"; +import { logger } from "@/src/utils/logger"; export const infestedFoundryController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -67,21 +68,43 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { const miscItemChanges: IMiscItem[] = []; let totalPercentagePointsGained = 0; + const currentUnixSeconds = Math.trunc(new Date().getTime() / 1000); + for (const contribution of request.ResourceContributions) { const snack = ExportMisc.helminthSnacks[contribution.ItemType]; - // Note: Currently ignoring loss of apetite - totalPercentagePointsGained += snack.gain * 100; // 30% would be gain=0.3, so percentage points is equal to gain * 100. - const resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type); - if (resource) { - resource.Count += Math.trunc(snack.gain * 1000); - } else { - inventory.InfestedFoundry.Resources.push({ - ItemType: snack.type, - Count: Math.trunc(snack.gain * 1000) // 30% would be gain=0.3 or Count=300, so Count=gain*1000. - }); + let resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type); + if (!resource) { + resource = + inventory.InfestedFoundry.Resources[ + inventory.InfestedFoundry.Resources.push({ ItemType: snack.type, Count: 0 }) - 1 + ]; } + resource.RecentlyConvertedResources ??= []; + let record = resource.RecentlyConvertedResources.find(x => x.ItemType == contribution.ItemType); + if (!record) { + record = + resource.RecentlyConvertedResources[ + resource.RecentlyConvertedResources.push({ ItemType: contribution.ItemType, Date: 0 }) - 1 + ]; + } + + const hoursRemaining = (record.Date - currentUnixSeconds) / 3600; + const apetiteFactor = apetiteModel(hoursRemaining) / 30; + logger.debug(`helminth eating ${contribution.ItemType} (+${(snack.gain * 100).toFixed(0)}%)`, { + hoursRemaining, + apetiteFactor + }); + if (hoursRemaining >= 18) { + record.Date = currentUnixSeconds + 72 * 60 * 60; + } else { + record.Date = currentUnixSeconds + 24 * 60 * 60; + } + + totalPercentagePointsGained += snack.gain * 100 * apetiteFactor; // 30% would be gain=0.3, so percentage points is equal to gain * 100. + resource.Count += Math.trunc(snack.gain * 1000 * apetiteFactor); // 30% would be gain=0.3 or Count=300, so Count=gain*1000. + // tally items for removal const change = miscItemChanges.find(x => x.ItemType == contribution.ItemType); if (change) { @@ -387,3 +410,38 @@ interface IHelminthInvigorationRequest { ResourceTypes: string[]; ResourceCosts: number[]; } + +// Hours remaining, percentage points gained (out of 30 total) +// 0, 30 +// 5, 25.8 +// 10, 21.6 +// 12, 20 +// 16, 16.6 +// 17, 15.8 +// 18, 15 +// 20, 15 +// 24, 15 +// 36, 15 +// 40, 13.6 +// 47, 11.3 +// 48, 11 +// 50, 10.3 +// 60, 7 +// 70, 3.6 +// 71, 3.3 +// 72, 3 +const apetiteModel = (x: number): number => { + if (x <= 0) { + return 30; + } + if (x < 18) { + return -0.84 * x + 30; + } + if (x <= 36) { + return 15; + } + if (x < 71.9) { + return -0.3327892 * x + 26.94135; + } + return 3; +}; diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index 7c8d5b75..8202bda9 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -45,7 +45,8 @@ import { ICrewShipMembers, ICrewShip, ICrewShipPilotWeapon, - IShipExterior + IShipExterior, + IHelminthFoodRecord } from "../../types/inventoryTypes/inventoryTypes"; import { IOid } from "../../types/commonTypes"; import { @@ -470,7 +471,22 @@ const consumedSchuitsSchema = new Schema( { _id: false } ); -const helminthResourceSchema = new Schema({ ItemType: String, Count: Number }, { _id: false }); +const helminthFoodRecordSchema = new Schema( + { + ItemType: String, + Date: Number + }, + { _id: false } +); + +const helminthResourceSchema = new Schema( + { + ItemType: String, + Count: Number, + RecentlyConvertedResources: { type: [helminthFoodRecordSchema], default: undefined } + }, + { _id: false } +); const infestedFoundrySchema = new Schema( { diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 6d6dd9bc..1f43c918 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -513,13 +513,15 @@ export interface IFusionTreasure { Sockets: number; } +export interface IHelminthFoodRecord { + ItemType: string; + Date: number; +} + export interface IHelminthResource { ItemType: string; Count: number; - RecentlyConvertedResources?: { - ItemType: string; - Date: number; - }[]; + RecentlyConvertedResources?: IHelminthFoodRecord[]; } export interface IInfestedFoundry { -- 2.47.2 From 9d115a4d02344a60ac64b35c66b8031804d15c9b Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 5 Jan 2025 13:40:19 +0100 Subject: [PATCH 05/10] feat: archon shard removal (#724) --- .../api/infestedFoundryController.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index d52c7f74..40a95d37 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -41,6 +41,25 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { break; } + case "x": { + // shard removal + const request = getJSONfromString(String(req.body)) as IShardUninstallRequest; + const inventory = await getInventory(accountId); + const suit = inventory.Suits.find(suit => suit._id.toString() == request.SuitId.$oid)!; + suit.ArchonCrystalUpgrades![request.Slot] = {}; + const bile = inventory.InfestedFoundry!.Resources!.find( + x => x.ItemType == "/Lotus/Types/Items/InfestedFoundry/HelminthBile" + )!; + bile.Count -= 300; + await inventory.save(); + res.json({ + InventoryChanges: { + InfestedFoundry: inventory.toJSON().InfestedFoundry + } + }); + break; + } + case "n": { // name the beast const request = getJSONfromString(String(req.body)) as IHelminthNameRequest; @@ -274,6 +293,11 @@ interface IShardInstallRequest { Color: string; } +interface IShardUninstallRequest { + SuitId: IOid; + Slot: number; +} + interface IHelminthNameRequest { newName: string; } -- 2.47.2 From 82621ebe0f3b02391ab9a94df291b0cf2d1bc0a5 Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 5 Jan 2025 23:20:36 +0100 Subject: [PATCH 06/10] feat: sentient apetite (#726) --- .../api/infestedFoundryController.ts | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index 40a95d37..ea35fa4d 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -3,7 +3,13 @@ import { getAccountIdForRequest } from "@/src/services/loginService"; import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getInventory, addMiscItems, updateCurrency, addRecipes } from "@/src/services/inventoryService"; import { IOid } from "@/src/types/commonTypes"; -import { IConsumedSuit, IInfestedFoundry, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; +import { + IConsumedSuit, + IHelminthFoodRecord, + IInfestedFoundry, + IMiscItem, + ITypeCount +} from "@/src/types/inventoryTypes/inventoryTypes"; import { ExportMisc, ExportRecipes } from "warframe-public-export-plus"; import { getRecipe } from "@/src/services/itemDataService"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; @@ -92,6 +98,34 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { for (const contribution of request.ResourceContributions) { const snack = ExportMisc.helminthSnacks[contribution.ItemType]; + // tally items for removal + const change = miscItemChanges.find(x => x.ItemType == contribution.ItemType); + if (change) { + change.ItemCount -= snack.count; + } else { + miscItemChanges.push({ ItemType: contribution.ItemType, ItemCount: snack.count * -1 }); + } + + if (snack.type == "/Lotus/Types/Items/InfestedFoundry/HelminthAppetiteCooldownReducer") { + // sentinent apetite + let mostDislikedSnackRecord: IHelminthFoodRecord = { ItemType: "", Date: 0 }; + for (const resource of inventory.InfestedFoundry.Resources) { + if (resource.RecentlyConvertedResources) { + for (const record of resource.RecentlyConvertedResources) { + if (record.Date > mostDislikedSnackRecord.Date) { + mostDislikedSnackRecord = record; + } + } + } + } + logger.debug("helminth eats sentient resource; most disliked snack:", { + type: mostDislikedSnackRecord.ItemType, + date: mostDislikedSnackRecord.Date + }); + mostDislikedSnackRecord.Date = currentUnixSeconds + 24 * 60 * 60; // Possibly unfaithful + continue; + } + let resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type); if (!resource) { resource = @@ -116,21 +150,13 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { apetiteFactor }); if (hoursRemaining >= 18) { - record.Date = currentUnixSeconds + 72 * 60 * 60; + record.Date = currentUnixSeconds + 72 * 60 * 60; // Possibly unfaithful } else { record.Date = currentUnixSeconds + 24 * 60 * 60; } totalPercentagePointsGained += snack.gain * 100 * apetiteFactor; // 30% would be gain=0.3, so percentage points is equal to gain * 100. resource.Count += Math.trunc(snack.gain * 1000 * apetiteFactor); // 30% would be gain=0.3 or Count=300, so Count=gain*1000. - - // tally items for removal - const change = miscItemChanges.find(x => x.ItemType == contribution.ItemType); - if (change) { - change.ItemCount -= snack.count; - } else { - miscItemChanges.push({ ItemType: contribution.ItemType, ItemCount: snack.count * -1 }); - } } const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry, 666 * totalPercentagePointsGained); @@ -435,6 +461,8 @@ interface IHelminthInvigorationRequest { ResourceCosts: number[]; } +// A fitted model for observed apetite values. Likely slightly inaccurate. +// // Hours remaining, percentage points gained (out of 30 total) // 0, 30 // 5, 25.8 -- 2.47.2 From eb6baa5e1550be8e6ba3678745154afaffb568ca Mon Sep 17 00:00:00 2001 From: Sainan Date: Mon, 6 Jan 2025 01:21:02 +0100 Subject: [PATCH 07/10] fix: removing an archon shard doesn't refund it (#729) --- .../api/infestedFoundryController.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index ea35fa4d..04144475 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -52,14 +52,33 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { const request = getJSONfromString(String(req.body)) as IShardUninstallRequest; const inventory = await getInventory(accountId); const suit = inventory.Suits.find(suit => suit._id.toString() == request.SuitId.$oid)!; + + // refund shard + const shard = Object.entries(colorToShard).find( + ([color]) => color == suit.ArchonCrystalUpgrades![request.Slot].Color + )![1]; + const miscItemChanges = [ + { + ItemType: shard, + ItemCount: 1 + } + ]; + addMiscItems(inventory, miscItemChanges); + + // remove from suit suit.ArchonCrystalUpgrades![request.Slot] = {}; + + // remove bile const bile = inventory.InfestedFoundry!.Resources!.find( x => x.ItemType == "/Lotus/Types/Items/InfestedFoundry/HelminthBile" )!; bile.Count -= 300; + await inventory.save(); + res.json({ InventoryChanges: { + MiscItems: miscItemChanges, InfestedFoundry: inventory.toJSON().InfestedFoundry } }); -- 2.47.2 From 709c2a401bf22d9014136771fb61e245e6192fe7 Mon Sep 17 00:00:00 2001 From: Sainan Date: Mon, 6 Jan 2025 01:21:37 +0100 Subject: [PATCH 08/10] feat: spectre loadouts (#719) --- .../api/claimCompletedRecipeController.ts | 24 +++++++++ src/controllers/api/startRecipeController.ts | 54 +++++++++++++++++++ src/models/inventoryModels/inventoryModel.ts | 21 ++++---- src/types/inventoryTypes/inventoryTypes.ts | 17 +++--- 4 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/controllers/api/claimCompletedRecipeController.ts b/src/controllers/api/claimCompletedRecipeController.ts index 5b8e048e..179327da 100644 --- a/src/controllers/api/claimCompletedRecipeController.ts +++ b/src/controllers/api/claimCompletedRecipeController.ts @@ -59,6 +59,30 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) = }); } else { logger.debug("Claiming Recipe", { recipe, pendingRecipe }); + + if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") { + const inventory = await getInventory(accountId); + inventory.PendingSpectreLoadouts ??= []; + inventory.SpectreLoadouts ??= []; + + const pendingLoadoutIndex = inventory.PendingSpectreLoadouts.findIndex( + x => x.ItemType == recipe.resultType + ); + if (pendingLoadoutIndex != -1) { + const loadoutIndex = inventory.SpectreLoadouts.findIndex(x => x.ItemType == recipe.resultType); + if (loadoutIndex != -1) { + inventory.SpectreLoadouts.splice(loadoutIndex, 1); + } + logger.debug( + "moving spectre loadout from pending to active", + inventory.toJSON().PendingSpectreLoadouts![pendingLoadoutIndex] + ); + inventory.SpectreLoadouts.push(inventory.PendingSpectreLoadouts[pendingLoadoutIndex]); + inventory.PendingSpectreLoadouts.splice(pendingLoadoutIndex, 1); + await inventory.save(); + } + } + let InventoryChanges = {}; if (recipe.consumeOnUse) { const recipeChanges = [ diff --git a/src/controllers/api/startRecipeController.ts b/src/controllers/api/startRecipeController.ts index 83501874..4a511315 100644 --- a/src/controllers/api/startRecipeController.ts +++ b/src/controllers/api/startRecipeController.ts @@ -6,6 +6,7 @@ import { getRecipe } from "@/src/services/itemDataService"; import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { unixTimesInMs } from "@/src/constants/timeConstants"; import { Types } from "mongoose"; +import { ISpectreLoadout } from "@/src/types/inventoryTypes/inventoryTypes"; interface IStartRecipeRequest { RecipeName: string; @@ -43,6 +44,59 @@ export const startRecipeController: RequestHandler = async (req, res) => { _id: new Types.ObjectId() }); + if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") { + const spectreLoadout: ISpectreLoadout = { + ItemType: recipe.resultType, + Suits: "", + LongGuns: "", + Pistols: "", + Melee: "" + }; + for ( + let secretIngredientsIndex = 0; + secretIngredientsIndex != recipe.secretIngredients!.length; + ++secretIngredientsIndex + ) { + const type = recipe.secretIngredients![secretIngredientsIndex].ItemType; + const oid = startRecipeRequest.Ids[recipe.ingredients.length + secretIngredientsIndex]; + if (oid == "ffffffffffffffffffffffff") { + // user chose to preserve the active loadout + break; + } + if (type == "/Lotus/Types/Game/PowerSuits/PlayerPowerSuit") { + const item = inventory.Suits.find(x => x._id.toString() == oid)!; + spectreLoadout.Suits = item.ItemType; + } else if (type == "/Lotus/Weapons/Tenno/Pistol/LotusPistol") { + const item = inventory.Pistols.find(x => x._id.toString() == oid)!; + spectreLoadout.Pistols = item.ItemType; + spectreLoadout.PistolsModularParts = item.ModularParts; + } else if (type == "/Lotus/Weapons/Tenno/LotusLongGun") { + const item = inventory.LongGuns.find(x => x._id.toString() == oid)!; + spectreLoadout.LongGuns = item.ItemType; + spectreLoadout.LongGunsModularParts = item.ModularParts; + } else { + console.assert(type == "/Lotus/Types/Game/LotusMeleeWeapon"); + const item = inventory.Melee.find(x => x._id.toString() == oid)!; + spectreLoadout.Melee = item.ItemType; + spectreLoadout.MeleeModularParts = item.ModularParts; + } + } + if ( + spectreLoadout.Suits != "" && + spectreLoadout.LongGuns != "" && + spectreLoadout.Pistols != "" && + spectreLoadout.Melee != "" + ) { + inventory.PendingSpectreLoadouts ??= []; + const existingIndex = inventory.PendingSpectreLoadouts.findIndex(x => x.ItemType == recipe.resultType); + if (existingIndex != -1) { + inventory.PendingSpectreLoadouts.splice(existingIndex, 1); + } + inventory.PendingSpectreLoadouts.push(spectreLoadout); + logger.debug("pending spectre loadout", spectreLoadout); + } + } + const newInventory = await inventory.save(); res.json({ diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index 8202bda9..f1e3edb6 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -548,13 +548,14 @@ const fusionTreasuresSchema = new Schema().add(typeCountSchema) const spectreLoadoutsSchema = new Schema( { - LongGuns: String, - Melee: String, - Pistols: String, - PistolsFeatures: Number, - PistolsModularParts: [String], + ItemType: String, Suits: String, - ItemType: String + LongGuns: String, + LongGunsModularParts: { type: [String], default: undefined }, + Pistols: String, + PistolsModularParts: { type: [String], default: undefined }, + Melee: String, + MeleeModularParts: { type: [String], default: undefined } }, { _id: false } ); @@ -936,11 +937,9 @@ const inventorySchema = new Schema( QualifyingInvasions: [Schema.Types.Mixed], FactionScores: [Number], - //Have only Suit+Pistols+LongGuns+Melee+ItemType(BronzeSpectre,GoldSpectre,PlatinumSpectreArmy,SilverSpectreArmy) - //"/Lotus/Types/Game/SpectreArmies/BronzeSpectreArmy": "Vapor Specter Regiment", - SpectreLoadouts: [spectreLoadoutsSchema], - //If you want change Spectre Gear id - PendingSpectreLoadouts: [Schema.Types.Mixed], + // https://warframe.fandom.com/wiki/Specter_(Tenno) + PendingSpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined }, + SpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined }, //New Quest Email EmailItems: [TypeXPItemSchema], diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 1f43c918..c453206d 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -203,8 +203,8 @@ export interface IInventoryResponse { SpaceMelee: IEquipmentDatabase[]; SpaceGuns: IEquipmentDatabase[]; ArchwingEnabled: boolean; - PendingSpectreLoadouts: any[]; - SpectreLoadouts: ISpectreLoadout[]; + PendingSpectreLoadouts?: ISpectreLoadout[]; + SpectreLoadouts?: ISpectreLoadout[]; SentinelWeapons: IEquipmentDatabase[]; Sentinels: IEquipmentDatabase[]; EmailItems: ITypeCount[]; @@ -871,13 +871,14 @@ export interface IShipInventory { } export interface ISpectreLoadout { - LongGuns: string; - Melee: string; - Pistols: string; - PistolsFeatures: number; - PistolsModularParts: string[]; - Suits: string; ItemType: string; + Suits: string; + LongGuns: string; + LongGunsModularParts?: string[]; + Pistols: string; + PistolsModularParts?: string[]; + Melee: string; + MeleeModularParts?: string[]; } export interface IStepSequencer { -- 2.47.2 From 69c65f3ce2d1f47f2285dfef80a47a7b87fa7d04 Mon Sep 17 00:00:00 2001 From: Sainan Date: Mon, 6 Jan 2025 04:36:40 +0100 Subject: [PATCH 09/10] fix: missing teshin hard mode vendor manifest (#737) --- src/services/serversideVendorsService.ts | 18 +- .../TeshinHardModeVendorManifest.json | 603 ++++++++++++++++++ 2 files changed, 613 insertions(+), 8 deletions(-) create mode 100644 static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json diff --git a/src/services/serversideVendorsService.ts b/src/services/serversideVendorsService.ts index 0b61caa4..3b4c3a61 100644 --- a/src/services/serversideVendorsService.ts +++ b/src/services/serversideVendorsService.ts @@ -15,16 +15,17 @@ import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json"; import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json"; import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json"; -import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json"; import HubsPerrinSequenceWeaponVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsPerrinSequenceWeaponVendorManifest.json"; +import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json"; import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json"; import OstronFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronFishmongerVendorManifest.json"; -import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json"; import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json"; -import SolarisFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisFishmongerVendorManifest.json"; -import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json"; +import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json"; import SolarisDebtTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorManifest.json"; import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json"; +import SolarisFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisFishmongerVendorManifest.json"; +import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json"; +import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json"; import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json"; interface IVendorManifest { @@ -55,16 +56,17 @@ const vendorManifests: IVendorManifest[] = [ EntratiLabsEntratiLabsCommisionsManifest, EntratiLabsEntratiLabVendorManifest, HubsIronwakeDondaVendorManifest, - HubsRailjackCrewMemberVendorManifest, HubsPerrinSequenceWeaponVendorManifest, + HubsRailjackCrewMemberVendorManifest, MaskSalesmanManifest, OstronFishmongerVendorManifest, - OstronProspectorVendorManifest, OstronPetVendorManifest, - SolarisFishmongerVendorManifest, - SolarisProspectorVendorManifest, + OstronProspectorVendorManifest, SolarisDebtTokenVendorManifest, SolarisDebtTokenVendorRepossessionsManifest, + SolarisFishmongerVendorManifest, + SolarisProspectorVendorManifest, + TeshinHardModeVendorManifest, ZarimanCommisionsManifestArchimedean ]; diff --git a/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json b/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json new file mode 100644 index 00000000..abfd1cab --- /dev/null +++ b/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json @@ -0,0 +1,603 @@ +{ + "VendorInfo":{ + "_id":{ + "$oid":"63ed01efbdaa38891767bac9" + }, + "TypeName":"/Lotus/Types/Game/VendorManifests/Hubs/TeshinHardModeVendorManifest", + "ItemManifest":[ + { + "StoreItem":"/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinArmsBlueprint", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9947" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinBodyBlueprint", + "ItemPrices":[ + { + "ItemCount":25, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9948" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinHeadBlueprint", + "ItemPrices":[ + { + "ItemCount":20, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9949" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinLegsBlueprint", + "ItemPrices":[ + { + "ItemCount":25, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e994a" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e994b" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e994c" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Recipes/Components/FormaStanceBlueprint", + "ItemPrices":[ + { + "ItemCount":10, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e994d" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Upgrades/Skins/Effects/OrbsEphemera", + "ItemPrices":[ + { + "ItemCount":3, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e994e" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Upgrades/Skins/Effects/TatsuSkullEphemera", + "ItemPrices":[ + { + "ItemCount":85, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e994f" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Upgrades/Mods/Randomized/RawShotgunRandomMod", + "ItemPrices":[ + { + "ItemCount":75, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9950" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Recipes/Components/UmbraFormaBlueprint", + "ItemPrices":[ + { + "ItemCount":150, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9951" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Items/MiscItems/Kuva", + "ItemPrices":[ + { + "ItemCount":55, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":50000, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9952" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Upgrades/Mods/Randomized/RawModularPistolRandomMod", + "ItemPrices":[ + { + "ItemCount":75, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9953" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Items/MiscItems/Forma", + "ItemPrices":[ + { + "ItemCount":75, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":3, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9954" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Upgrades/Mods/Randomized/RawModularMeleeRandomMod", + "ItemPrices":[ + { + "ItemCount":75, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9955" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Upgrades/Mods/FusionBundles/EvergreenLoginRewardFusionBundle", + "ItemPrices":[ + { + "ItemCount":150, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9956" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Upgrades/Mods/Randomized/RawRifleRandomMod", + "ItemPrices":[ + { + "ItemCount":75, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9957" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Upgrades/Mods/Shotgun/WeaponRecoilReductionMod", + "ItemPrices":[ + { + "ItemCount":35, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9958" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Items/ShipDecos/TeshinBobbleHead", + "ItemPrices":[ + { + "ItemCount":35, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e9959" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/StoreItems/AvatarImages/ImageGaussVED", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e995a" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/StoreItems/AvatarImages/ImageGrendelVED", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e995b" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageProteaAction", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e995c" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Items/ShipDecos/TeaSet", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e995d" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageXakuAction", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e995e" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Items/MiscItems/RivenIdentifier", + "ItemPrices":[ + { + "ItemCount":20, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"2051240400000" + } + }, + "PurchaseQuantityLimit":1, + "RotatedWeekly":true, + "AllowMultipurchase":false, + "Id":{ + "$oid":"66fd60b20ba592c4c95e995f" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/BoosterPacks/RandomSyndicateProjectionPack", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":1, + "Expiry":{ + "$date":{ + "$numberLong":"1736726400000" + } + }, + "PurchaseQuantityLimit":25, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e997c" + } + }, + { + "StoreItem":"/Lotus/StoreItems/Types/Items/MiscItems/Kuva", + "ItemPrices":[ + { + "ItemCount":15, + "ItemType":"/Lotus/Types/Items/MiscItems/SteelEssence", + "ProductCategory":"MiscItems" + } + ], + "Bin":"BIN_0", + "QuantityMultiplier":10000, + "Expiry":{ + "$date":{ + "$numberLong":"1736726400000" + } + }, + "PurchaseQuantityLimit":25, + "AllowMultipurchase":true, + "Id":{ + "$oid":"66fd60b20ba592c4c95e997d" + } + } + ], + "PropertyTextHash":"0A0F20AFA748FBEE490510DBF5A33A0D", + "Expiry":{ + "$date":{ + "$numberLong":"1736726400000" + } + } + } +} \ No newline at end of file -- 2.47.2 From 05fd3c4cecf671d43938a9f5698bed072a4fc6e5 Mon Sep 17 00:00:00 2001 From: Sainan Date: Mon, 6 Jan 2025 05:26:37 +0100 Subject: [PATCH 10/10] chore: some notes in inventoryTypes --- src/types/inventoryTypes/inventoryTypes.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index c453206d..81d12bff 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -102,7 +102,8 @@ export type TSolarMapRegion = | "Uranus" | "Venus" | "Void" - | "SolarMapDeimosName"; + | "SolarMapDeimosName" + | "1999MapName"; //TODO: perhaps split response and database into their own files @@ -727,6 +728,8 @@ export interface IPendingRecipe { ItemType: string; CompletionDate: Date; ItemId: IOid; + TargetItemId?: string; // likely related to liches + TargetFingerprint?: string; // likely related to liches } export interface IPendingTrade { -- 2.47.2