This commit is contained in:
nrbdev 2025-01-06 05:16:29 -05:00
commit c3065e38c2
25 changed files with 1151 additions and 101 deletions

View File

@ -17,6 +17,8 @@
"completeAllQuests": true,
"infiniteCredits": true,
"infinitePlatinum": true,
"infiniteEndo": true,
"infiniteRegalAya": true,
"unlockAllShipFeatures": true,
"unlockAllShipDecorations": true,
"unlockAllFlavourItems": true,

8
package-lock.json generated
View File

@ -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",

View File

@ -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"

View File

@ -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<string, string[]> = {
"/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"
]
};

View File

@ -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 = [

View File

@ -0,0 +1,99 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { getRandomWeightedReward2 } from "@/src/services/rngService";
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
import { ExportRelics, ExportRewards, TRarity } from "warframe-public-export-plus";
export const getVoidProjectionRewardsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const data = getJSONfromString(String(req.body)) as IVoidProjectionRewardRequest;
const response: IVoidProjectionRewardResponse = {
CurrentWave: data.CurrentWave,
ParticipantInfo: data.ParticipantInfo,
DifficultyTier: data.DifficultyTier
};
if (data.ParticipantInfo.QualifiesForReward) {
const relic = ExportRelics[data.ParticipantInfo.VoidProjection];
const weights = refinementToWeights[relic.quality];
logger.debug(`opening a relic of quality ${relic.quality}; rarity weights are`, weights);
const reward = getRandomWeightedReward2(
ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics
weights
)!;
logger.debug(`relic rolled`, reward);
response.ParticipantInfo.Reward = reward.type;
// Remove relic
const inventory = await getInventory(accountId);
addMiscItems(inventory, [
{
ItemType: data.ParticipantInfo.VoidProjection,
ItemCount: -1
}
]);
await inventory.save();
// Give reward
await handleStoreItemAcquisition(reward.type, accountId, reward.itemCount);
}
res.json(response);
};
const refinementToWeights = {
VPQ_BRONZE: {
COMMON: 0.76,
UNCOMMON: 0.22,
RARE: 0.02,
LEGENDARY: 0
},
VPQ_SILVER: {
COMMON: 0.7,
UNCOMMON: 0.26,
RARE: 0.04,
LEGENDARY: 0
},
VPQ_GOLD: {
COMMON: 0.6,
UNCOMMON: 0.34,
RARE: 0.06,
LEGENDARY: 0
},
VPQ_PLATINUM: {
COMMON: 0.5,
UNCOMMON: 0.4,
RARE: 0.1,
LEGENDARY: 0
}
};
interface IVoidProjectionRewardRequest {
CurrentWave: number;
ParticipantInfo: IParticipantInfo;
VoidTier: string;
DifficultyTier: number;
VoidProjectionRemovalHash: string;
}
interface IVoidProjectionRewardResponse {
CurrentWave: number;
ParticipantInfo: IParticipantInfo;
DifficultyTier: number;
}
interface IParticipantInfo {
AccountId: string;
Name: string;
ChosenRewardOwner: string;
MissionHash: string;
VoidProjection: string;
Reward: string;
QualifiesForReward: boolean;
HaveRewardResponse: boolean;
RewardsMultiplier: number;
RewardProjection: string;
HardModeReward: ITypeCount;
}

View File

@ -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";
@ -46,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
}
});
@ -92,6 +117,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 +169,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 +480,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

View File

@ -74,6 +74,12 @@ export const inventoryController: RequestHandler = async (request, response) =>
inventoryResponse.PremiumCreditsFree = 999999999;
inventoryResponse.PremiumCredits = 999999999;
}
if (config.infiniteEndo) {
inventoryResponse.FusionPoints = 999999999;
}
if (config.infiniteRegalAya) {
inventoryResponse.PrimeTokens = 999999999;
}
if (config.skipAllDialogue) {
inventoryResponse.TauntHistory = [

View File

@ -0,0 +1,31 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IPlayerSkills } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express";
export const playerSkillsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const request = getJSONfromString(String(req.body)) as IPlayerSkillsRequest;
const oldRank: number = inventory.PlayerSkills[request.Skill as keyof IPlayerSkills];
const cost = (request.Pool == "LPP_DRIFTER" ? drifterCosts[oldRank] : 1 << oldRank) * 1000;
inventory.PlayerSkills[request.Pool as keyof IPlayerSkills] -= cost;
inventory.PlayerSkills[request.Skill as keyof IPlayerSkills]++;
await inventory.save();
res.json({
Pool: request.Pool,
PoolInc: -cost,
Skill: request.Skill,
Rank: oldRank + 1
});
};
interface IPlayerSkillsRequest {
Pool: string;
Skill: string;
}
const drifterCosts = [20, 25, 30, 45, 65, 90, 125, 160, 205, 255];

View File

@ -1,5 +1,4 @@
import { RequestHandler } from "express";
import { ISellRequest } from "@/src/types/sellTypes";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory, addMods, addRecipes, addMiscItems, addConsumables } from "@/src/services/inventoryService";
@ -100,3 +99,30 @@ export const sellController: RequestHandler = async (req, res) => {
await inventory.save();
res.json({});
};
interface ISellRequest {
Items: {
Suits?: ISellItem[];
LongGuns?: ISellItem[];
Pistols?: ISellItem[];
Melee?: ISellItem[];
Consumables?: ISellItem[];
Recipes?: ISellItem[];
Upgrades?: ISellItem[];
MiscItems?: ISellItem[];
};
SellPrice: number;
SellCurrency:
| "SC_RegularCredits"
| "SC_PrimeBucks"
| "SC_FusionPoints"
| "SC_DistillPoints"
| "SC_CrewShipFusionPoints"
| "SC_Resources";
buildLabel: string;
}
interface ISellItem {
String: string; // oid or uniqueName
Count: number;
}

View File

@ -2,11 +2,23 @@ import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getPersonalRooms } from "@/src/services/personalRoomsService";
import { TBootLocation } from "@/src/types/shipTypes";
import { getInventory } from "@/src/services/inventoryService";
export const setBootLocationController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const personalRooms = await getPersonalRooms(accountId);
personalRooms.Ship.BootLocation = req.query.bootLocation as string as TBootLocation;
await personalRooms.save();
if (personalRooms.Ship.BootLocation == "SHOP") {
// Temp fix so the motorcycle in the backroom doesn't appear broken.
// This code may be removed when quests are fully implemented.
const inventory = await getInventory(accountId);
if (inventory.Motorcycles.length == 0) {
inventory.Motorcycles.push({ ItemType: "/Lotus/Types/Vehicles/Motorcycle/MotorcyclePowerSuit" });
await inventory.save();
}
}
res.end();
};

View File

@ -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({

View File

@ -1,29 +1,21 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { ExportSyndicates } from "warframe-public-export-plus";
import { ExportSyndicates, ISyndicateSacrifice } from "warframe-public-export-plus";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { getInventory } from "@/src/services/inventoryService";
import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
export const syndicateSacrificeController: RequestHandler = async (request, response) => {
const accountId = await getAccountIdForRequest(request);
const inventory = await getInventory(accountId);
const data = getJSONfromString(String(request.body)) as ISyndicateSacrifice;
const data = getJSONfromString(String(request.body)) as ISyndicateSacrificeRequest;
let syndicate = inventory.Affiliations.find(x => x.Tag == data.AffiliationTag);
if (!syndicate) {
syndicate = inventory.Affiliations[inventory.Affiliations.push({ Tag: data.AffiliationTag, Standing: 0 }) - 1];
}
let reward: string | undefined;
const manifest = ExportSyndicates[data.AffiliationTag];
if (manifest?.initiationReward && data.SacrificeLevel == 0) {
reward = manifest.initiationReward;
syndicate.Initiated = true;
}
const level = data.SacrificeLevel - (syndicate.Title ?? 0);
const res: ISyndicateSacrificeResponse = {
AffiliationTag: data.AffiliationTag,
@ -33,18 +25,43 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
NewEpisodeReward: syndicate?.Tag == "RadioLegionIntermission9Syndicate"
};
const manifest = ExportSyndicates[data.AffiliationTag];
let sacrifice: ISyndicateSacrifice | undefined;
let reward: string | undefined;
if (data.SacrificeLevel == 0) {
sacrifice = manifest.initiationSacrifice;
reward = manifest.initiationReward;
syndicate.Initiated = true;
} else {
sacrifice = manifest.titles?.find(x => x.level == data.SacrificeLevel)?.sacrifice;
}
if (sacrifice) {
res.InventoryChanges = { ...updateCurrency(inventory, sacrifice.credits, false) };
const miscItemChanges = sacrifice.items.map(x => ({
ItemType: x.ItemType,
ItemCount: x.ItemCount * -1
}));
addMiscItems(inventory, miscItemChanges);
res.InventoryChanges.MiscItems = miscItemChanges;
}
if (syndicate?.Title !== undefined) syndicate.Title += 1;
await inventory.save();
if (reward) {
res.InventoryChanges = (await handleStoreItemAcquisition(reward, accountId)).InventoryChanges;
combineInventoryChanges(
res.InventoryChanges,
(await handleStoreItemAcquisition(reward, accountId)).InventoryChanges
);
}
response.json(res);
};
interface ISyndicateSacrifice {
interface ISyndicateSacrificeRequest {
AffiliationTag: string;
SacrificeLevel: number;
AllowMultiple: boolean;

View File

@ -439,18 +439,17 @@ const seasonChallengeHistorySchema = new Schema<ISeasonChallenge>(
//TODO: check whether this is complete
const playerSkillsSchema = new Schema<IPlayerSkills>(
{
LPP_SPACE: Number,
LPP_DRIFTER: Number,
LPS_NONE: Number,
LPS_PILOTING: Number,
LPS_GUNNERY: Number,
LPS_TACTICAL: Number,
LPS_ENGINEERING: Number,
LPS_COMMAND: Number,
LPS_DRIFT_COMBAT: Number,
LPS_DRIFT_RIDING: Number,
LPS_DRIFT_OPPORTUNITY: Number,
LPS_DRIFT_ENDURANCE: Number
LPP_SPACE: { type: Number, default: 0 },
LPS_PILOTING: { type: Number, default: 0 },
LPS_GUNNERY: { type: Number, default: 0 },
LPS_TACTICAL: { type: Number, default: 0 },
LPS_ENGINEERING: { type: Number, default: 0 },
LPS_COMMAND: { type: Number, default: 0 },
LPP_DRIFTER: { type: Number, default: 0 },
LPS_DRIFT_COMBAT: { type: Number, default: 0 },
LPS_DRIFT_RIDING: { type: Number, default: 0 },
LPS_DRIFT_OPPORTUNITY: { type: Number, default: 0 },
LPS_DRIFT_ENDURANCE: { type: Number, default: 0 }
},
{ _id: false }
);
@ -548,13 +547,14 @@ const fusionTreasuresSchema = new Schema<IFusionTreasure>().add(typeCountSchema)
const spectreLoadoutsSchema = new Schema<ISpectreLoadout>(
{
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 +936,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
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],
@ -1019,7 +1017,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
//Modulars lvl and exp(Railjack|Duviri)
//https://warframe.fandom.com/wiki/Intrinsics
PlayerSkills: playerSkillsSchema,
PlayerSkills: { type: playerSkillsSchema, default: {} },
//TradeBannedUntil data
TradeBannedUntil: Schema.Types.Mixed,

View File

@ -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";
@ -26,6 +27,7 @@ import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsers
import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController";
import { getShipController } from "@/src/controllers/api/getShipController";
import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController";
import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
import { guildTechController } from "../controllers/api/guildTechController";
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
@ -44,6 +46,7 @@ import { missionInventoryUpdateController } from "@/src/controllers/api/missionI
import { modularWeaponCraftingController } from "@/src/controllers/api/modularWeaponCraftingController";
import { modularWeaponSaleController } from "@/src/controllers/api/modularWeaponSaleController";
import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
import { playerSkillsController } from "@/src/controllers/api/playerSkillsController";
import { projectionManagerController } from "../controllers/api/projectionManagerController";
import { purchaseController } from "@/src/controllers/api/purchaseController";
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
@ -107,6 +110,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);
@ -120,6 +124,7 @@ apiRouter.post("/focus.php", focusController);
apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
apiRouter.post("/genericUpdate.php", genericUpdateController);
apiRouter.post("/getAlliance.php", getAllianceController);
apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
apiRouter.post("/gildWeapon.php", gildWeaponController);
apiRouter.post("/guildTech.php", guildTechController);
apiRouter.post("/hostSession.php", hostSessionController);
@ -130,6 +135,7 @@ apiRouter.post("/login.php", loginController);
apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController);
apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController);
apiRouter.post("/nameWeapon.php", nameWeaponController);
apiRouter.post("/playerSkills.php", playerSkillsController);
apiRouter.post("/projectionManager.php", projectionManagerController);
apiRouter.post("/purchase.php", purchaseController);
apiRouter.post("/rerollRandomMod.php", rerollRandomModController);

View File

@ -43,6 +43,8 @@ interface IConfig {
completeAllQuests?: boolean;
infiniteCredits?: boolean;
infinitePlatinum?: boolean;
infiniteEndo?: boolean;
infiniteRegalAya?: boolean;
unlockAllShipFeatures?: boolean;
unlockAllShipDecorations?: boolean;
unlockAllFlavourItems?: boolean;

View File

@ -908,8 +908,12 @@ export const upgradeMod = async (artifactsData: IArtifactsRequest, accountId: st
}
}
inventory.RegularCredits -= Cost;
inventory.FusionPoints -= FusionPointCost;
if (!config.infiniteCredits) {
inventory.RegularCredits -= Cost;
}
if (!config.infiniteEndo) {
inventory.FusionPoints -= FusionPointCost;
}
const changedInventory = await inventory.save();
const itemId = changedInventory.toJSON().Upgrades[itemIndex]?.ItemId?.$oid;

View File

@ -24,6 +24,7 @@ import {
ExportVendors,
TRarity
} from "warframe-public-export-plus";
import { config } from "./configService";
export const getStoreItemCategory = (storeItem: string): string => {
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
@ -152,7 +153,7 @@ export const handlePurchase = async (
purchaseResponse.InventoryChanges.MiscItems ??= [];
(purchaseResponse.InventoryChanges.MiscItems as IMiscItem[]).push(invItem);
} else {
} else if (!config.infiniteRegalAya) {
inventory.PrimeTokens -= offer.PrimePrice! * purchaseRequest.PurchaseParams.Quantity;
}
await inventory.save();

View File

@ -6,6 +6,18 @@ export interface IRngResult {
probability: number;
}
export const getRandomElement = <T>(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;
@ -40,3 +52,22 @@ export const getRandomWeightedReward = (
}
return getRandomReward(resultPool);
};
export const getRandomWeightedReward2 = (
pool: { type: string; itemCount: number; rarity: TRarity }[],
weights: Record<TRarity, number>
): IRngResult | undefined => {
const resultPool: IRngResult[] = [];
const rarityCounts: Record<TRarity, number> = { COMMON: 0, UNCOMMON: 0, RARE: 0, LEGENDARY: 0 };
for (const entry of pool) {
++rarityCounts[entry.rarity];
}
for (const entry of pool) {
resultPool.push({
type: entry.type,
itemCount: entry.itemCount,
probability: weights[entry.rarity] / rarityCounts[entry.rarity]
});
}
return getRandomReward(resultPool);
};

View File

@ -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
];

View File

@ -102,7 +102,8 @@ export type TSolarMapRegion =
| "Uranus"
| "Venus"
| "Void"
| "SolarMapDeimosName";
| "SolarMapDeimosName"
| "1999MapName";
//TODO: perhaps split response and database into their own files
@ -203,8 +204,8 @@ export interface IInventoryResponse {
SpaceMelee: IEquipmentDatabase[];
SpaceGuns: IEquipmentDatabase[];
ArchwingEnabled: boolean;
PendingSpectreLoadouts: any[];
SpectreLoadouts: ISpectreLoadout[];
PendingSpectreLoadouts?: ISpectreLoadout[];
SpectreLoadouts?: ISpectreLoadout[];
SentinelWeapons: IEquipmentDatabase[];
Sentinels: IEquipmentDatabase[];
EmailItems: ITypeCount[];
@ -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 {
@ -816,13 +819,12 @@ export interface IPersonalTechProject {
export interface IPlayerSkills {
LPP_SPACE: number;
LPP_DRIFTER: number;
LPS_NONE: number;
LPS_PILOTING: number;
LPS_GUNNERY: number;
LPS_TACTICAL: number;
LPS_ENGINEERING: number;
LPS_COMMAND: number;
LPP_DRIFTER: number;
LPS_DRIFT_COMBAT: number;
LPS_DRIFT_RIDING: number;
LPS_DRIFT_OPPORTUNITY: number;
@ -871,13 +873,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 {

View File

@ -1,26 +0,0 @@
export interface ISellRequest {
Items: {
Suits?: ISellItem[];
LongGuns?: ISellItem[];
Pistols?: ISellItem[];
Melee?: ISellItem[];
Consumables?: ISellItem[];
Recipes?: ISellItem[];
Upgrades?: ISellItem[];
MiscItems?: ISellItem[];
};
SellPrice: number;
SellCurrency:
| "SC_RegularCredits"
| "SC_PrimeBucks"
| "SC_FusionPoints"
| "SC_DistillPoints"
| "SC_CrewShipFusionPoints"
| "SC_Resources";
buildLabel: string;
}
export interface ISellItem {
String: string; // oid or uniqueName
Count: number;
}

View File

@ -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"
}
}
}
}

View File

@ -235,6 +235,14 @@
<input class="form-check-input" type="checkbox" id="infinitePlatinum" />
<label class="form-check-label" for="infinitePlatinum">Infinite Platinum</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="infiniteEndo" />
<label class="form-check-label" for="infiniteEndo">Infinite Endo</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="infiniteRegalAya" />
<label class="form-check-label" for="infiniteRegalAya">Infinite Regal Aya</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllShipFeatures" />
<label class="form-check-label" for="unlockAllShipFeatures">Unlock All Ship Features</label>

View File

@ -805,6 +805,8 @@ const uiConfigs = [
"completeAllQuests",
"infiniteCredits",
"infinitePlatinum",
"infiniteEndo",
"infiniteRegalAya",
"unlockAllShipFeatures",
"unlockAllShipDecorations",
"unlockAllFlavourItems",