chore: fix controllers exporting non-RequestHandler things (#1468)

I'm surprised JavaScript allows circular includes, but they still don't feel good.

Reviewed-on: OpenWF/SpaceNinjaServer#1468
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit is contained in:
Sainan 2025-04-05 06:52:35 -07:00 committed by Sainan
parent 62263efde3
commit 49edebc1eb
18 changed files with 243 additions and 256 deletions

View File

@ -19,7 +19,7 @@ import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
export interface IClaimCompletedRecipeRequest { interface IClaimCompletedRecipeRequest {
RecipeIds: IOid[]; RecipeIds: IOid[];
} }

View File

@ -1,4 +1,5 @@
import { Inventory } from "@/src/models/inventoryModels/inventoryModel"; import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { generateRewardSeed } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
@ -18,9 +19,3 @@ export const getNewRewardSeedController: RequestHandler = async (req, res) => {
); );
res.json({ rewardSeed: rewardSeed }); res.json({ rewardSeed: rewardSeed });
}; };
export function generateRewardSeed(): number {
const min = -Number.MAX_SAFE_INTEGER;
const max = Number.MAX_SAFE_INTEGER;
return Math.floor(Math.random() * (max - min + 1)) + min;
}

View File

@ -2,8 +2,8 @@ import { RequestHandler } from "express";
import { parseString } from "@/src/helpers/general"; import { parseString } from "@/src/helpers/general";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { IGroup } from "@/src/types/loginTypes";
import { giveKeyChainItem } from "@/src/services/questService"; import { giveKeyChainItem } from "@/src/services/questService";
import { IKeyChainRequest } from "@/src/types/requestTypes";
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => { export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
const accountId = parseString(req.query.accountId); const accountId = parseString(req.query.accountId);
@ -15,9 +15,3 @@ export const giveKeyChainTriggeredItemsController: RequestHandler = async (req,
res.send(inventoryChanges); res.send(inventoryChanges);
}; };
export interface IKeyChainRequest {
KeyChain: string;
ChainStage: number;
Groups?: IGroup[];
}

View File

@ -1,7 +1,7 @@
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
import { getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { giveKeyChainMessage } from "@/src/services/questService"; import { giveKeyChainMessage } from "@/src/services/questService";
import { IKeyChainRequest } from "@/src/types/requestTypes";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => { export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {

View File

@ -20,11 +20,11 @@ export const giveQuestKeyRewardController: RequestHandler = async (req, res) =>
//TODO: consider whishlist changes //TODO: consider whishlist changes
}; };
export interface IQuestKeyRewardRequest { interface IQuestKeyRewardRequest {
reward: IQuestKeyReward; reward: IQuestKeyReward;
} }
export interface IQuestKeyReward { interface IQuestKeyReward {
RewardType: string; RewardType: string;
CouponType: string; CouponType: string;
Icon: string; Icon: string;

View File

@ -1,20 +1,8 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { InventoryDocumentProps } from "@/src/models/inventoryModels/inventoryModel"; import { addStartingGear, getInventory } from "@/src/services/inventoryService";
import {
addEquipment,
addItem,
addPowerSuit,
combineInventoryChanges,
getInventory,
updateSlots
} from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { IInventoryClient, IInventoryDatabase, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; import { TPartialStartingGear } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { HydratedDocument } from "mongoose";
type TPartialStartingGear = Pick<IInventoryClient, "LongGuns" | "Suits" | "Pistols" | "Melee">;
export const giveStartingGearController: RequestHandler = async (req, res) => { export const giveStartingGearController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -26,72 +14,3 @@ export const giveStartingGearController: RequestHandler = async (req, res) => {
res.send(inventoryChanges); res.send(inventoryChanges);
}; };
//TODO: RawUpgrades might need to return a LastAdded
const awakeningRewards = [
"/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem1",
"/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2",
"/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3",
"/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4",
"/Lotus/Types/Restoratives/LisetAutoHack",
"/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod"
];
export const addStartingGear = async (
inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>,
startingGear: TPartialStartingGear | undefined = undefined
): Promise<IInventoryChanges> => {
const { LongGuns, Pistols, Suits, Melee } = startingGear || {
LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }],
Suits: [{ ItemType: "/Lotus/Powersuits/Excalibur/Excalibur" }],
Melee: [{ ItemType: "/Lotus/Weapons/Tenno/Melee/LongSword/LongSword" }]
};
//TODO: properly merge weapon bin changes it is currently static here
const inventoryChanges: IInventoryChanges = {};
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges);
addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges);
addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges);
await addPowerSuit(inventory, Suits[0].ItemType, inventoryChanges);
addEquipment(
inventory,
"DataKnives",
"/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon",
undefined,
inventoryChanges,
{ XP: 450_000 }
);
addEquipment(
inventory,
"Scoops",
"/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest",
undefined,
inventoryChanges
);
updateSlots(inventory, InventorySlot.SUITS, 0, 1);
updateSlots(inventory, InventorySlot.WEAPONS, 0, 3);
inventoryChanges.SuitBin = { count: 1, platinum: 0, Slots: -1 };
inventoryChanges.WeaponBin = { count: 3, platinum: 0, Slots: -3 };
await addItem(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain");
inventory.ActiveQuest = "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain";
inventory.PremiumCredits = 50;
inventory.PremiumCreditsFree = 50;
inventoryChanges.PremiumCredits = 50;
inventoryChanges.PremiumCreditsFree = 50;
inventory.RegularCredits = 3000;
inventoryChanges.RegularCredits = 3000;
for (const item of awakeningRewards) {
const inventoryDelta = await addItem(inventory, item);
combineInventoryChanges(inventoryChanges, inventoryDelta);
}
inventory.PlayedParkourTutorial = true;
inventory.ReceivedStartingGear = true;
return inventoryChanges;
};

View File

@ -6,20 +6,21 @@ import { IOid } from "@/src/types/commonTypes";
import { import {
IConsumedSuit, IConsumedSuit,
IHelminthFoodRecord, IHelminthFoodRecord,
IInfestedFoundryClient,
IInfestedFoundryDatabase,
IInventoryClient, IInventoryClient,
IMiscItem, IMiscItem,
InventorySlot, InventorySlot
ITypeCount
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { ExportMisc, ExportRecipes } from "warframe-public-export-plus"; import { ExportMisc } from "warframe-public-export-plus";
import { getRecipe } from "@/src/services/itemDataService"; import { getRecipe } from "@/src/services/itemDataService";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { toMongoDate } from "@/src/helpers/inventoryHelpers"; import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { colorToShard } from "@/src/helpers/shardHelper"; import { colorToShard } from "@/src/helpers/shardHelper";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
import {
addInfestedFoundryXP,
applyCheatsToInfestedFoundry,
handleSubsumeCompletion
} from "@/src/services/infestedFoundryService";
export const infestedFoundryController: RequestHandler = async (req, res) => { export const infestedFoundryController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -383,116 +384,11 @@ interface IHelminthFeedRequest {
}[]; }[];
} }
export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundryDatabase, delta: number): ITypeCount[] => {
const recipeChanges: ITypeCount[] = [];
infestedFoundry.XP ??= 0;
const prevXP = infestedFoundry.XP;
infestedFoundry.XP += delta;
if (prevXP < 2250_00 && infestedFoundry.XP >= 2250_00) {
infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 3;
}
if (prevXP < 5625_00 && infestedFoundry.XP >= 5625_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldsBlueprint",
ItemCount: 1
});
}
if (prevXP < 10125_00 && infestedFoundry.XP >= 10125_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthHackBlueprint", ItemCount: 1 });
}
if (prevXP < 15750_00 && infestedFoundry.XP >= 15750_00) {
infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 10;
}
if (prevXP < 22500_00 && infestedFoundry.XP >= 22500_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthAmmoEfficiencyBlueprint",
ItemCount: 1
});
}
if (prevXP < 30375_00 && infestedFoundry.XP >= 30375_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStunBlueprint", ItemCount: 1 });
}
if (prevXP < 39375_00 && infestedFoundry.XP >= 39375_00) {
infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 20;
}
if (prevXP < 60750_00 && infestedFoundry.XP >= 60750_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStatusBlueprint", ItemCount: 1 });
}
if (prevXP < 73125_00 && infestedFoundry.XP >= 73125_00) {
infestedFoundry.Slots = 1;
}
if (prevXP < 86625_00 && infestedFoundry.XP >= 86625_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldArmorBlueprint",
ItemCount: 1
});
}
if (prevXP < 101250_00 && infestedFoundry.XP >= 101250_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthProcBlockBlueprint",
ItemCount: 1
});
}
if (prevXP < 117000_00 && infestedFoundry.XP >= 117000_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthEnergyShareBlueprint",
ItemCount: 1
});
}
if (prevXP < 133875_00 && infestedFoundry.XP >= 133875_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthMaxStatusBlueprint",
ItemCount: 1
});
}
if (prevXP < 151875_00 && infestedFoundry.XP >= 151875_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthTreasureBlueprint",
ItemCount: 1
});
}
return recipeChanges;
};
interface IHelminthSubsumeRequest { interface IHelminthSubsumeRequest {
SuitId: IOid; SuitId: IOid;
Recipe: string; Recipe: string;
} }
export const handleSubsumeCompletion = (inventory: TInventoryDatabaseDocument): ITypeCount[] => {
const [recipeType] = Object.entries(ExportRecipes).find(
([_recipeType, recipe]) =>
recipe.secretIngredientAction == "SIA_WARFRAME_ABILITY" &&
recipe.secretIngredients![0].ItemType == inventory.InfestedFoundry!.LastConsumedSuit!.ItemType
)!;
inventory.InfestedFoundry!.LastConsumedSuit = undefined;
inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = undefined;
const recipeChanges: ITypeCount[] = [
{
ItemType: recipeType,
ItemCount: 1
}
];
addRecipes(inventory, recipeChanges);
return recipeChanges;
};
export const applyCheatsToInfestedFoundry = (infestedFoundry: IInfestedFoundryClient): void => {
if (config.infiniteHelminthMaterials) {
infestedFoundry.Resources = [
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthCalx", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBiotics", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthSynthetics", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthPheromones", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBile", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthOxides", Count: 1000 }
];
}
};
interface IHelminthOfferingsUpdate { interface IHelminthOfferingsUpdate {
OfferingsIndex: number; OfferingsIndex: number;
SuitTypes: string[]; SuitTypes: string[];

View File

@ -13,9 +13,10 @@ import {
ExportResources, ExportResources,
ExportVirtuals ExportVirtuals
} from "warframe-public-export-plus"; } from "warframe-public-export-plus";
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "./infestedFoundryController"; import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
import { addMiscItems, allDailyAffiliationKeys, createLibraryDailyTask } from "@/src/services/inventoryService"; import { addMiscItems, allDailyAffiliationKeys, createLibraryDailyTask } from "@/src/services/inventoryService";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { catBreadHash } from "@/src/helpers/stringHelpers";
export const inventoryController: RequestHandler = async (request, response) => { export const inventoryController: RequestHandler = async (request, response) => {
const accountId = await getAccountIdForRequest(request); const accountId = await getAccountIdForRequest(request);
@ -269,7 +270,7 @@ export const getInventoryResponse = async (
return inventoryResponse; return inventoryResponse;
}; };
export const addString = (arr: string[], str: string): void => { const addString = (arr: string[], str: string): void => {
if (!arr.find(x => x == str)) { if (!arr.find(x => x == str)) {
arr.push(str); arr.push(str);
} }
@ -299,13 +300,3 @@ const resourceGetParent = (resourceName: string): string | undefined => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
return ExportVirtuals[resourceName]?.parentName; return ExportVirtuals[resourceName]?.parentName;
}; };
// This is FNV1a-32 except operating under modulus 2^31 because JavaScript is stinky and likes producing negative integers out of nowhere.
export const catBreadHash = (name: string): number => {
let hash = 2166136261;
for (let i = 0; i != name.length; ++i) {
hash = (hash ^ name.charCodeAt(i)) & 0x7fffffff;
hash = (hash * 16777619) & 0x7fffffff;
}
return hash;
};

View File

@ -11,7 +11,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getRecipeByResult } from "@/src/services/itemDataService"; import { getRecipeByResult } from "@/src/services/itemDataService";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { addInfestedFoundryXP, applyCheatsToInfestedFoundry } from "./infestedFoundryController"; import { addInfestedFoundryXP, applyCheatsToInfestedFoundry } from "@/src/services/infestedFoundryService";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
export const upgradesController: RequestHandler = async (req, res) => { export const upgradesController: RequestHandler = async (req, res) => {

View File

@ -18,7 +18,7 @@ import {
ITypeXPItem ITypeXPItem
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { catBreadHash } from "../api/inventoryController"; import { catBreadHash } from "@/src/helpers/stringHelpers";
import { ExportCustoms, ExportDojoRecipes } from "warframe-public-export-plus"; import { ExportCustoms, ExportDojoRecipes } from "warframe-public-export-plus";
import { IStatsClient } from "@/src/types/statTypes"; import { IStatsClient } from "@/src/types/statTypes";
import { toStoreItem } from "@/src/services/itemDataService"; import { toStoreItem } from "@/src/services/itemDataService";

View File

@ -27,6 +27,16 @@ export const getIndexAfter = (str: string, searchWord: string): number => {
return index + searchWord.length; return index + searchWord.length;
}; };
// This is FNV1a-32 except operating under modulus 2^31 because JavaScript is stinky and likes producing negative integers out of nowhere.
export const catBreadHash = (name: string): number => {
let hash = 2166136261;
for (let i = 0; i != name.length; ++i) {
hash = (hash ^ name.charCodeAt(i)) & 0x7fffffff;
hash = (hash * 16777619) & 0x7fffffff;
}
return hash;
};
export const regexEscape = (str: string): string => { export const regexEscape = (str: string): string => {
str = str.split(".").join("\\."); str = str.split(".").join("\\.");
str = str.split("\\").join("\\\\"); str = str.split("\\").join("\\\\");

View File

@ -0,0 +1,110 @@
import { ExportRecipes } from "warframe-public-export-plus";
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
import { IInfestedFoundryClient, IInfestedFoundryDatabase, ITypeCount } from "../types/inventoryTypes/inventoryTypes";
import { addRecipes } from "./inventoryService";
import { config } from "./configService";
export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundryDatabase, delta: number): ITypeCount[] => {
const recipeChanges: ITypeCount[] = [];
infestedFoundry.XP ??= 0;
const prevXP = infestedFoundry.XP;
infestedFoundry.XP += delta;
if (prevXP < 2250_00 && infestedFoundry.XP >= 2250_00) {
infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 3;
}
if (prevXP < 5625_00 && infestedFoundry.XP >= 5625_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldsBlueprint",
ItemCount: 1
});
}
if (prevXP < 10125_00 && infestedFoundry.XP >= 10125_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthHackBlueprint", ItemCount: 1 });
}
if (prevXP < 15750_00 && infestedFoundry.XP >= 15750_00) {
infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 10;
}
if (prevXP < 22500_00 && infestedFoundry.XP >= 22500_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthAmmoEfficiencyBlueprint",
ItemCount: 1
});
}
if (prevXP < 30375_00 && infestedFoundry.XP >= 30375_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStunBlueprint", ItemCount: 1 });
}
if (prevXP < 39375_00 && infestedFoundry.XP >= 39375_00) {
infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 20;
}
if (prevXP < 60750_00 && infestedFoundry.XP >= 60750_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStatusBlueprint", ItemCount: 1 });
}
if (prevXP < 73125_00 && infestedFoundry.XP >= 73125_00) {
infestedFoundry.Slots = 1;
}
if (prevXP < 86625_00 && infestedFoundry.XP >= 86625_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldArmorBlueprint",
ItemCount: 1
});
}
if (prevXP < 101250_00 && infestedFoundry.XP >= 101250_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthProcBlockBlueprint",
ItemCount: 1
});
}
if (prevXP < 117000_00 && infestedFoundry.XP >= 117000_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthEnergyShareBlueprint",
ItemCount: 1
});
}
if (prevXP < 133875_00 && infestedFoundry.XP >= 133875_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthMaxStatusBlueprint",
ItemCount: 1
});
}
if (prevXP < 151875_00 && infestedFoundry.XP >= 151875_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthTreasureBlueprint",
ItemCount: 1
});
}
return recipeChanges;
};
export const handleSubsumeCompletion = (inventory: TInventoryDatabaseDocument): ITypeCount[] => {
const [recipeType] = Object.entries(ExportRecipes).find(
([_recipeType, recipe]) =>
recipe.secretIngredientAction == "SIA_WARFRAME_ABILITY" &&
recipe.secretIngredients![0].ItemType == inventory.InfestedFoundry!.LastConsumedSuit!.ItemType
)!;
inventory.InfestedFoundry!.LastConsumedSuit = undefined;
inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = undefined;
const recipeChanges: ITypeCount[] = [
{
ItemType: recipeType,
ItemCount: 1
}
];
addRecipes(inventory, recipeChanges);
return recipeChanges;
};
export const applyCheatsToInfestedFoundry = (infestedFoundry: IInfestedFoundryClient): void => {
if (config.infiniteHelminthMaterials) {
infestedFoundry.Resources = [
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthCalx", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBiotics", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthSynthetics", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthPheromones", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBile", Count: 1000 },
{ ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthOxides", Count: 1000 }
];
}
};

View File

@ -1,10 +1,6 @@
import { import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
Inventory,
InventoryDocumentProps,
TInventoryDatabaseDocument
} from "@/src/models/inventoryModels/inventoryModel";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
import { HydratedDocument, Types } from "mongoose"; import { Types } from "mongoose";
import { SlotNames, IInventoryChanges, IBinChanges, slotNames } from "@/src/types/purchaseTypes"; import { SlotNames, IInventoryChanges, IBinChanges, slotNames } from "@/src/types/purchaseTypes";
import { import {
IChallengeProgress, IChallengeProgress,
@ -19,16 +15,16 @@ import {
TEquipmentKey, TEquipmentKey,
IFusionTreasure, IFusionTreasure,
IDailyAffiliations, IDailyAffiliations,
IInventoryDatabase,
IKubrowPetEggDatabase, IKubrowPetEggDatabase,
IKubrowPetEggClient, IKubrowPetEggClient,
ILibraryDailyTaskInfo, ILibraryDailyTaskInfo,
ICalendarProgress, ICalendarProgress,
IDroneClient, IDroneClient,
IUpgradeClient IUpgradeClient,
TPartialStartingGear
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate"; import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
import { IMissionInventoryUpdateRequest } from "../types/requestTypes"; import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { convertInboxMessage, fromStoreItem, getKeyChainItems } from "@/src/services/itemDataService"; import { convertInboxMessage, fromStoreItem, getKeyChainItems } from "@/src/services/itemDataService";
import { import {
@ -62,10 +58,7 @@ import {
TStandingLimitBin TStandingLimitBin
} from "warframe-public-export-plus"; } from "warframe-public-export-plus";
import { createShip } from "./shipService"; import { createShip } from "./shipService";
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
import { toOid } from "../helpers/inventoryHelpers"; import { toOid } from "../helpers/inventoryHelpers";
import { generateRewardSeed } from "@/src/controllers/api/getNewRewardSeedController";
import { addStartingGear } from "@/src/controllers/api/giveStartingGearController";
import { addQuestKey, completeQuest } from "@/src/services/questService"; import { addQuestKey, completeQuest } from "@/src/services/questService";
import { handleBundleAcqusition } from "./purchaseService"; import { handleBundleAcqusition } from "./purchaseService";
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json"; import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
@ -114,6 +107,81 @@ export const createInventory = async (
} }
}; };
export const generateRewardSeed = (): number => {
const min = -Number.MAX_SAFE_INTEGER;
const max = Number.MAX_SAFE_INTEGER;
return Math.floor(Math.random() * (max - min + 1)) + min;
};
//TODO: RawUpgrades might need to return a LastAdded
const awakeningRewards = [
"/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem1",
"/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2",
"/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3",
"/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4",
"/Lotus/Types/Restoratives/LisetAutoHack",
"/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod"
];
export const addStartingGear = async (
inventory: TInventoryDatabaseDocument,
startingGear: TPartialStartingGear | undefined = undefined
): Promise<IInventoryChanges> => {
const { LongGuns, Pistols, Suits, Melee } = startingGear || {
LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }],
Suits: [{ ItemType: "/Lotus/Powersuits/Excalibur/Excalibur" }],
Melee: [{ ItemType: "/Lotus/Weapons/Tenno/Melee/LongSword/LongSword" }]
};
//TODO: properly merge weapon bin changes it is currently static here
const inventoryChanges: IInventoryChanges = {};
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges);
addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges);
addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges);
await addPowerSuit(inventory, Suits[0].ItemType, inventoryChanges);
addEquipment(
inventory,
"DataKnives",
"/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon",
undefined,
inventoryChanges,
{ XP: 450_000 }
);
addEquipment(
inventory,
"Scoops",
"/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest",
undefined,
inventoryChanges
);
updateSlots(inventory, InventorySlot.SUITS, 0, 1);
updateSlots(inventory, InventorySlot.WEAPONS, 0, 3);
inventoryChanges.SuitBin = { count: 1, platinum: 0, Slots: -1 };
inventoryChanges.WeaponBin = { count: 3, platinum: 0, Slots: -3 };
await addItem(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain");
inventory.ActiveQuest = "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain";
inventory.PremiumCredits = 50;
inventory.PremiumCreditsFree = 50;
inventoryChanges.PremiumCredits = 50;
inventoryChanges.PremiumCreditsFree = 50;
inventory.RegularCredits = 3000;
inventoryChanges.RegularCredits = 3000;
for (const item of awakeningRewards) {
const inventoryDelta = await addItem(inventory, item);
combineInventoryChanges(inventoryChanges, inventoryDelta);
}
inventory.PlayedParkourTutorial = true;
inventory.ReceivedStartingGear = true;
return inventoryChanges;
};
/** /**
* Combines two inventory changes objects into one. * Combines two inventory changes objects into one.
* *
@ -1302,7 +1370,7 @@ export const addBooster = (ItemType: string, time: number, inventory: TInventory
}; };
export const updateSyndicate = ( export const updateSyndicate = (
inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>, inventory: TInventoryDatabaseDocument,
syndicateUpdate: IMissionInventoryUpdateRequest["AffiliationChanges"] syndicateUpdate: IMissionInventoryUpdateRequest["AffiliationChanges"]
): void => { ): void => {
syndicateUpdate?.forEach(affiliation => { syndicateUpdate?.forEach(affiliation => {

View File

@ -1,4 +1,4 @@
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; import { IKeyChainRequest } from "@/src/types/requestTypes";
import { getIndexAfter } from "@/src/helpers/stringHelpers"; import { getIndexAfter } from "@/src/helpers/stringHelpers";
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";

View File

@ -10,7 +10,7 @@ import {
import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes"; import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { IRngResult, getRandomElement, getRandomReward } from "@/src/services/rngService"; import { IRngResult, getRandomElement, getRandomReward } from "@/src/services/rngService";
import { equipmentKeys, IInventoryDatabase, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes"; import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
import { import {
addBooster, addBooster,
addChallenges, addChallenges,
@ -32,10 +32,10 @@ import {
updateSyndicate updateSyndicate
} from "@/src/services/inventoryService"; } from "@/src/services/inventoryService";
import { updateQuestKey } from "@/src/services/questService"; import { updateQuestKey } from "@/src/services/questService";
import { HydratedDocument, Types } from "mongoose"; import { Types } from "mongoose";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { getLevelKeyRewards, toStoreItem } from "@/src/services/itemDataService"; import { getLevelKeyRewards, toStoreItem } from "@/src/services/itemDataService";
import { InventoryDocumentProps, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { getEntriesUnsafe } from "@/src/utils/ts-utils"; import { getEntriesUnsafe } from "@/src/utils/ts-utils";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { handleStoreItemAcquisition } from "./purchaseService"; import { handleStoreItemAcquisition } from "./purchaseService";
@ -73,7 +73,7 @@ const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => {
//const knownUnhandledKeys: readonly string[] = ["test"] as const; // for unimplemented but important keys //const knownUnhandledKeys: readonly string[] = ["test"] as const; // for unimplemented but important keys
export const addMissionInventoryUpdates = async ( export const addMissionInventoryUpdates = async (
inventory: HydratedDocument<IInventoryDatabase, InventoryDocumentProps>, inventory: TInventoryDatabaseDocument,
inventoryUpdates: IMissionInventoryUpdateRequest inventoryUpdates: IMissionInventoryUpdateRequest
): Promise<IInventoryChanges> => { ): Promise<IInventoryChanges> => {
const inventoryChanges: IInventoryChanges = {}; const inventoryChanges: IInventoryChanges = {};
@ -661,7 +661,7 @@ interface IMissionCredits {
//creditBonus is not entirely accurate. //creditBonus is not entirely accurate.
//TODO: consider ActiveBoosters //TODO: consider ActiveBoosters
export const addCredits = ( export const addCredits = (
inventory: HydratedDocument<IInventoryDatabase>, inventory: TInventoryDatabaseDocument,
{ {
missionDropCredits, missionDropCredits,
missionCompletionCredits, missionCompletionCredits,

View File

@ -1,4 +1,4 @@
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; import { IKeyChainRequest } from "@/src/types/requestTypes";
import { isEmptyObject } from "@/src/helpers/general"; import { isEmptyObject } from "@/src/helpers/general";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { createMessage } from "@/src/services/inboxService"; import { createMessage } from "@/src/services/inboxService";
@ -9,14 +9,9 @@ import {
getLevelKeyRewards, getLevelKeyRewards,
getQuestCompletionItems getQuestCompletionItems
} from "@/src/services/itemDataService"; } from "@/src/services/itemDataService";
import { import { IQuestKeyClient, IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
IInventoryDatabase,
IQuestKeyClient,
IQuestKeyDatabase,
IQuestStage
} from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { HydratedDocument, Types } from "mongoose"; import { Types } from "mongoose";
import { ExportKeys } from "warframe-public-export-plus"; import { ExportKeys } from "warframe-public-export-plus";
import { addFixedLevelRewards } from "./missionInventoryUpdateService"; import { addFixedLevelRewards } from "./missionInventoryUpdateService";
import { IInventoryChanges } from "../types/purchaseTypes"; import { IInventoryChanges } from "../types/purchaseTypes";
@ -31,7 +26,7 @@ export interface IUpdateQuestRequest {
} }
export const updateQuestKey = async ( export const updateQuestKey = async (
inventory: HydratedDocument<IInventoryDatabase>, inventory: TInventoryDatabaseDocument,
questKeyUpdate: IUpdateQuestRequest["QuestKeys"] questKeyUpdate: IUpdateQuestRequest["QuestKeys"]
): Promise<IInventoryChanges> => { ): Promise<IInventoryChanges> => {
if (questKeyUpdate.length > 1) { if (questKeyUpdate.length > 1) {
@ -45,7 +40,7 @@ export const updateQuestKey = async (
throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`); throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`);
} }
inventory.QuestKeys[questKeyIndex] = questKeyUpdate[0]; inventory.QuestKeys[questKeyIndex].overwrite(questKeyUpdate[0]);
let inventoryChanges: IInventoryChanges = {}; let inventoryChanges: IInventoryChanges = {};
if (questKeyUpdate[0].Completed) { if (questKeyUpdate[0].Completed) {
@ -57,12 +52,12 @@ export const updateQuestKey = async (
logger.debug(`quest completion items`, questCompletionItems); logger.debug(`quest completion items`, questCompletionItems);
if (questCompletionItems) { if (questCompletionItems) {
inventoryChanges = await addItems(inventory as TInventoryDatabaseDocument, questCompletionItems); inventoryChanges = await addItems(inventory, questCompletionItems);
} }
inventory.ActiveQuest = ""; inventory.ActiveQuest = "";
if (questKeyUpdate[0].ItemType == "/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain") { if (questKeyUpdate[0].ItemType == "/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain") {
setupKahlSyndicate(inventory as TInventoryDatabaseDocument); setupKahlSyndicate(inventory);
} }
} }
return inventoryChanges; return inventoryChanges;

View File

@ -1168,3 +1168,5 @@ export interface ILockedWeaponGroupDatabase {
m?: Types.ObjectId; m?: Types.ObjectId;
sn?: Types.ObjectId; sn?: Types.ObjectId;
} }
export type TPartialStartingGear = Pick<IInventoryClient, "LongGuns" | "Suits" | "Pistols" | "Melee">;

View File

@ -21,6 +21,7 @@ import {
ILockedWeaponGroupClient, ILockedWeaponGroupClient,
ILoadOutPresets ILoadOutPresets
} from "./inventoryTypes/inventoryTypes"; } from "./inventoryTypes/inventoryTypes";
import { IGroup } from "./loginTypes";
export interface IAffiliationChange { export interface IAffiliationChange {
Tag: string; Tag: string;
@ -175,3 +176,9 @@ export interface IVoidTearParticipantInfo {
RewardProjection: string; RewardProjection: string;
HardModeReward: ITypeCount; HardModeReward: ITypeCount;
} }
export interface IKeyChainRequest {
KeyChain: string;
ChainStage: number;
Groups?: IGroup[];
}