Merge remote-tracking branch 'upstream/main' into worldstate

This commit is contained in:
dutlist 2024-06-19 11:21:28 +02:00
commit 96e80475bf
27 changed files with 483 additions and 7285 deletions

8
package-lock.json generated
View File

@ -13,7 +13,7 @@
"express": "^5.0.0-beta.3", "express": "^5.0.0-beta.3",
"mongoose": "^8.1.1", "mongoose": "^8.1.1",
"warframe-items": "^1.1262.74", "warframe-items": "^1.1262.74",
"warframe-public-export-plus": "^0.2.3", "warframe-public-export-plus": "^0.2.5",
"warframe-riven-info": "^0.1.0", "warframe-riven-info": "^0.1.0",
"winston": "^3.11.0", "winston": "^3.11.0",
"winston-daily-rotate-file": "^4.7.1" "winston-daily-rotate-file": "^4.7.1"
@ -3909,9 +3909,9 @@
} }
}, },
"node_modules/warframe-public-export-plus": { "node_modules/warframe-public-export-plus": {
"version": "0.2.3", "version": "0.2.5",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.2.3.tgz", "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.2.5.tgz",
"integrity": "sha512-Bl4gb3f1LIdGXLEOJg2XTIFYqrialdTIvVhDqDzVJIRfii0PKsy9jsr9vqM14tWz7oVpQMeCUyvisDkkXijTSg==" "integrity": "sha512-IsS2Z14CeTpGSpfeUxqTi8wAQjQ6qjh2kV8RC9St5hcDmII3NpwEFXmStEqz7r+JPfea72D3cZMMl+4QLHqvXw=="
}, },
"node_modules/warframe-riven-info": { "node_modules/warframe-riven-info": {
"version": "0.1.0", "version": "0.1.0",

View File

@ -17,7 +17,7 @@
"express": "^5.0.0-beta.3", "express": "^5.0.0-beta.3",
"mongoose": "^8.1.1", "mongoose": "^8.1.1",
"warframe-items": "^1.1262.74", "warframe-items": "^1.1262.74",
"warframe-public-export-plus": "^0.2.3", "warframe-public-export-plus": "^0.2.5",
"warframe-riven-info": "^0.1.0", "warframe-riven-info": "^0.1.0",
"winston": "^3.11.0", "winston": "^3.11.0",
"winston-daily-rotate-file": "^4.7.1" "winston-daily-rotate-file": "^4.7.1"

View File

@ -3,11 +3,11 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { getItemByBlueprint } from "@/src/services/itemDataService"; import { getRecipe } from "@/src/services/itemDataService";
import { IOid } from "@/src/types/commonTypes"; import { IOid } from "@/src/types/commonTypes";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory, updateCurrency, addItem } from "@/src/services/inventoryService"; import { getInventory, updateCurrency, addItem, addMiscItems, addRecipes } from "@/src/services/inventoryService";
export interface IClaimCompletedRecipeRequest { export interface IClaimCompletedRecipeRequest {
RecipeIds: IOid[]; RecipeIds: IOid[];
@ -37,25 +37,51 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
inventory.PendingRecipes.pull(pendingRecipe._id); inventory.PendingRecipes.pull(pendingRecipe._id);
await inventory.save(); await inventory.save();
const buildable = getItemByBlueprint(pendingRecipe.ItemType); const recipe = getRecipe(pendingRecipe.ItemType);
if (!buildable) { if (!recipe) {
logger.error(`no completed item found for recipe ${pendingRecipe._id}`); logger.error(`no completed item found for recipe ${pendingRecipe._id}`);
throw new Error(`no completed item found for recipe ${pendingRecipe._id}`); throw new Error(`no completed item found for recipe ${pendingRecipe._id}`);
} }
if (req.query.cancel) { if (req.query.cancel) {
// TODO: Refund items const currencyChanges = await updateCurrency(recipe.buildPrice * -1, false, accountId);
res.json({});
const inventory = await getInventory(accountId);
addMiscItems(inventory, recipe.ingredients);
await inventory.save();
// Not a bug: In the specific case of cancelling a recipe, InventoryChanges are expected to be the root.
res.json({
...currencyChanges,
MiscItems: recipe.ingredients
});
} else { } else {
logger.debug("Claiming Recipe", { buildable, pendingRecipe }); logger.debug("Claiming Recipe", { recipe, pendingRecipe });
let currencyChanges = {}; let InventoryChanges = {};
if (req.query.rush && buildable.skipBuildTimePrice) { if (recipe.consumeOnUse) {
currencyChanges = await updateCurrency(buildable.skipBuildTimePrice, true, accountId); const recipeChanges = [
{
ItemType: pendingRecipe.ItemType,
ItemCount: -1
}
];
InventoryChanges = { ...InventoryChanges, Recipes: recipeChanges };
const inventory = await getInventory(accountId);
addRecipes(inventory, recipeChanges);
await inventory.save();
}
if (req.query.rush) {
InventoryChanges = {
...InventoryChanges,
...(await updateCurrency(recipe.skipBuildTimePrice, true, accountId))
};
} }
res.json({ res.json({
InventoryChanges: { InventoryChanges: {
...currencyChanges, ...InventoryChanges,
...(await addItem(accountId, buildable.uniqueName, buildable.buildQuantity)).InventoryChanges ...(await addItem(accountId, recipe.resultType, recipe.num)).InventoryChanges
} }
}); });
} }

View File

@ -0,0 +1,88 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory, addMiscItems } from "@/src/services/inventoryService";
import { IOid } from "@/src/types/commonTypes";
export const infestedFoundryController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const payload = getJSONfromString(req.body.toString());
switch (req.query.mode) {
case "s": {
// shard installation
const request = payload as IShardInstallRequest;
const inventory = await getInventory(accountId);
const suit = inventory.Suits.find(suit => suit._id.toString() == request.SuitId.$oid)!;
if (
!suit.ArchonCrystalUpgrades ||
suit.ArchonCrystalUpgrades.length != 5 // we shouldn't have an array like this, but older inventories may disagree...
) {
suit.ArchonCrystalUpgrades = [{}, {}, {}, {}, {}];
}
suit.ArchonCrystalUpgrades[request.Slot] = {
UpgradeType: request.UpgradeType,
Color: request.Color
};
const miscItemChanges = [
{
ItemType: colorToShard[request.Color],
ItemCount: -1
}
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
res.json({
InventoryChanges: {
MiscItems: miscItemChanges
}
});
break;
}
case "n": {
// name the beast
const inventory = await getInventory(accountId);
inventory.InfestedFoundry ??= {};
inventory.InfestedFoundry.Name = payload.newName as string;
await inventory.save();
res.json({
InventoryChanges: {
InfestedFoundry: {
Name: inventory.InfestedFoundry.Name
}
}
});
break;
}
case "o": // offerings update
// {"OfferingsIndex":540,"SuitTypes":["/Lotus/Powersuits/PaxDuviricus/PaxDuviricusBaseSuit","/Lotus/Powersuits/Nezha/NezhaBaseSuit","/Lotus/Powersuits/Devourer/DevourerBaseSuit"],"Extra":false}
res.status(404).end();
break;
default:
throw new Error(`unhandled infestedFoundry mode: ${req.query.mode}`);
}
};
interface IShardInstallRequest {
SuitId: IOid;
Slot: number;
UpgradeType: string;
Color: string;
}
const colorToShard: Record<string, string> = {
ACC_RED: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalAmar",
ACC_RED_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalAmarMythic",
ACC_YELLOW: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalNira",
ACC_YELLOW_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalNiraMythic",
ACC_BLUE: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalBoreal",
ACC_BLUE_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalBorealMythic",
ACC_GREEN: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalGreen",
ACC_GREEN_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalGreenMythic",
ACC_ORANGE: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalOrange",
ACC_ORANGE_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalOrangeMythic",
ACC_PURPLE: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalViolet",
ACC_PURPLE_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalVioletMythic"
};

View File

@ -5,12 +5,9 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { Request, RequestHandler, Response } from "express"; import { Request, RequestHandler, Response } from "express";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
import allMissions from "@/static/fixed_responses/allMissions.json"; import allMissions from "@/static/fixed_responses/allMissions.json";
import allQuestKeys from "@/static/fixed_responses/allQuestKeys.json";
import allShipDecorations from "@/static/fixed_responses/allShipDecorations.json";
import allFlavourItems from "@/static/fixed_responses/allFlavourItems.json";
import allSkins from "@/static/fixed_responses/allSkins.json";
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes"; import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
import { IShipInventory, IFlavourItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { IShipInventory } from "@/src/types/inventoryTypes/inventoryTypes";
import { ExportCustoms, ExportFlavour, ExportKeys, ExportResources } from "warframe-public-export-plus";
const inventoryController: RequestHandler = async (request: Request, response: Response) => { const inventoryController: RequestHandler = async (request: Request, response: Response) => {
let accountId; let accountId;
@ -51,9 +48,11 @@ const inventoryController: RequestHandler = async (request: Request, response: R
} }
if (config.unlockAllQuests) { if (config.unlockAllQuests) {
for (const questKey of allQuestKeys) { for (const [k, v] of Object.entries(ExportKeys)) {
if (!inventoryResponse.QuestKeys.find(quest => quest.ItemType == questKey)) { if ("chainStages" in v) {
inventoryResponse.QuestKeys.push({ ItemType: questKey }); if (!inventoryResponse.QuestKeys.find(quest => quest.ItemType == k)) {
inventoryResponse.QuestKeys.push({ ItemType: k });
}
} }
} }
} }
@ -76,17 +75,30 @@ const inventoryController: RequestHandler = async (request: Request, response: R
inventoryResponse.NodeIntrosCompleted.push("/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level"); inventoryResponse.NodeIntrosCompleted.push("/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level");
} }
if (config.unlockAllShipDecorations) inventoryResponse.ShipDecorations = allShipDecorations; if (config.unlockAllShipDecorations) {
if (config.unlockAllFlavourItems) inventoryResponse.FlavourItems = allFlavourItems satisfies IFlavourItem[]; inventoryResponse.ShipDecorations = [];
for (const [uniqueName, item] of Object.entries(ExportResources)) {
if (item.productCategory == "ShipDecorations") {
inventoryResponse.ShipDecorations.push({ ItemType: uniqueName, ItemCount: 1 });
}
}
}
if (config.unlockAllFlavourItems) {
inventoryResponse.FlavourItems = [];
for (const uniqueName in ExportFlavour) {
inventoryResponse.FlavourItems.push({ ItemType: uniqueName });
}
}
if (config.unlockAllSkins) { if (config.unlockAllSkins) {
inventoryResponse.WeaponSkins = []; inventoryResponse.WeaponSkins = [];
for (const skin of allSkins) { for (const uniqueName in ExportCustoms) {
inventoryResponse.WeaponSkins.push({ inventoryResponse.WeaponSkins.push({
ItemId: { ItemId: {
$oid: "000000000000000000000000" $oid: "000000000000000000000000"
}, },
ItemType: skin ItemType: uniqueName
}); });
} }
} }

View File

@ -43,7 +43,7 @@ const loginController: RequestHandler = async (request, response) => {
platformCDNs: platformCDNs, platformCDNs: platformCDNs,
NRS: [config.myAddress], NRS: [config.myAddress],
DTLS: DTLS, DTLS: DTLS,
IRC: [config.myAddress], IRC: config.myIrcAddresses ?? [config.myAddress],
HUB: HUB, HUB: HUB,
BuildLabel: buildConfig.buildLabel, BuildLabel: buildConfig.buildLabel,
MatchmakingBuildId: buildConfig.matchmakingBuildId MatchmakingBuildId: buildConfig.matchmakingBuildId
@ -79,7 +79,7 @@ const loginController: RequestHandler = async (request, response) => {
platformCDNs: platformCDNs, platformCDNs: platformCDNs,
NRS: [config.myAddress], NRS: [config.myAddress],
DTLS: DTLS, DTLS: DTLS,
IRC: [config.myAddress], IRC: config.myIrcAddresses ?? [config.myAddress],
HUB: HUB, HUB: HUB,
BuildLabel: buildConfig.buildLabel, BuildLabel: buildConfig.buildLabel,
MatchmakingBuildId: buildConfig.matchmakingBuildId MatchmakingBuildId: buildConfig.matchmakingBuildId

View File

@ -1,6 +1,6 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { missionInventoryUpdate } from "@/src/services/inventoryService"; import { missionInventoryUpdate } from "@/src/services/inventoryService";
import { combineRewardAndLootInventory, getRewards } from "@/src/services/missionInventoryUpdateService "; import { combineRewardAndLootInventory, getRewards } from "@/src/services/missionInventoryUpdateService";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes"; import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";

View File

@ -0,0 +1,55 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { WeaponTypeInternal } from "@/src/services/itemDataService";
import { getInventory, updateCurrency, addWeapon, addMiscItems } from "@/src/services/inventoryService";
const modularWeaponTypes: Record<string, WeaponTypeInternal> = {
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam": "LongGuns",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": "Pistols",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": "Pistols",
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": "Pistols",
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": "Melee",
"/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon": "OperatorAmps"
};
interface IModularCraftRequest {
WeaponType: string;
Parts: string[];
}
export const modularWeaponCraftingController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const data: IModularCraftRequest = getJSONfromString(req.body.toString());
if (!(data.WeaponType in modularWeaponTypes)) {
throw new Error(`unknown modular weapon type: ${data.WeaponType}`);
}
const category = modularWeaponTypes[data.WeaponType];
// Give weapon
const weapon = await addWeapon(category, data.WeaponType, accountId, data.Parts);
// Remove 4000 credits
const currencyChanges = await updateCurrency(4000, false, accountId);
// Remove parts
const miscItemChanges = [];
for (const part of data.Parts) {
miscItemChanges.push({
ItemType: part,
ItemCount: -1
});
}
const inventory = await getInventory(accountId);
addMiscItems(inventory, miscItemChanges);
await inventory.save();
// Tell client what we did
res.json({
InventoryChanges: {
...currencyChanges,
[category]: [weapon],
MiscItems: miscItemChanges
}
});
};

View File

@ -1,7 +1,7 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { ISellRequest } from "@/src/types/sellTypes"; import { ISellRequest } from "@/src/types/sellTypes";
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory, addMods } from "@/src/services/inventoryService"; import { getInventory, addMods, addRecipes } from "@/src/services/inventoryService";
export const sellController: RequestHandler = async (req, res) => { export const sellController: RequestHandler = async (req, res) => {
const payload: ISellRequest = JSON.parse(req.body.toString()); const payload: ISellRequest = JSON.parse(req.body.toString());
@ -39,8 +39,14 @@ export const sellController: RequestHandler = async (req, res) => {
}); });
} }
if (payload.Items.Recipes) { if (payload.Items.Recipes) {
// TODO const recipeChanges = [];
// Note: sellItem.String is a uniqueName in this case for (const sellItem of payload.Items.Recipes) {
recipeChanges.push({
ItemType: sellItem.String,
ItemCount: sellItem.Count * -1
});
}
addRecipes(inventory, recipeChanges);
} }
if (payload.Items.Upgrades) { if (payload.Items.Upgrades) {
payload.Items.Upgrades.forEach(sellItem => { payload.Items.Upgrades.forEach(sellItem => {

View File

@ -0,0 +1,14 @@
import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import { IStepSequencer } from "@/src/types/inventoryTypes/inventoryTypes";
export const stepSequencersController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const stepSequencer = JSON.parse(req.body.toString()) as IStepSequencer;
delete stepSequencer.ItemId;
const stepSequencerIndex = inventory.StepSequencers.push(stepSequencer);
const changedInventory = await inventory.save();
res.json(changedInventory.StepSequencers[stepSequencerIndex - 1]); // unsure about the expected response format, but it seems anything works.
};

View File

@ -1,7 +1,7 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { MinItem, MinWeapon, warframes, items, getEnglishString } from "@/src/services/itemDataService"; import { MinItem, items, getEnglishString } from "@/src/services/itemDataService";
import badItems from "@/static/json/exclude-mods.json"; import badItems from "@/static/json/exclude-mods.json";
import { ExportArcanes, ExportWeapons } from "warframe-public-export-plus"; import { ExportArcanes, ExportResources, ExportWarframes, ExportWeapons } from "warframe-public-export-plus";
interface ListedItem { interface ListedItem {
uniqueName: string; uniqueName: string;
@ -20,6 +20,30 @@ function reduceItems(items: MinItem[]): ListedItem[] {
} }
const getItemListsController: RequestHandler = (_req, res) => { const getItemListsController: RequestHandler = (_req, res) => {
const weapons = [];
const miscitems = [];
for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
if (item.productCategory !== "OperatorAmps") {
if (item.totalDamage !== 0) {
weapons.push({
uniqueName,
name: getEnglishString(item.name)
});
} else if (!item.excludeFromCodex) {
miscitems.push({
uniqueName,
name: getEnglishString(item.name)
});
}
}
}
for (const [uniqueName, item] of Object.entries(ExportResources)) {
miscitems.push({
uniqueName,
name: getEnglishString(item.name)
});
}
const mods = reduceItems(items.filter(item => item.category == "Mods")); const mods = reduceItems(items.filter(item => item.category == "Mods"));
for (const [uniqueName, arcane] of Object.entries(ExportArcanes)) { for (const [uniqueName, arcane] of Object.entries(ExportArcanes)) {
mods.push({ mods.push({
@ -27,25 +51,18 @@ const getItemListsController: RequestHandler = (_req, res) => {
name: getEnglishString(arcane.name) name: getEnglishString(arcane.name)
}); });
} }
res.json({ res.json({
warframes: reduceItems(warframes), warframes: Object.entries(ExportWarframes)
weapons: Object.entries(ExportWeapons) .filter(([_uniqueName, warframe]) => warframe.productCategory == "Suits")
.filter(([_uniqueName, weapon]) => weapon.productCategory !== "OperatorAmps" && weapon.totalDamage !== 0) .map(([uniqueName, warframe]) => {
.map(([uniqueName, weapon]) => {
return { return {
uniqueName, uniqueName,
name: getEnglishString(weapon.name) name: getEnglishString(warframe.name)
}; };
}), }),
miscitems: reduceItems( weapons,
items.filter( miscitems,
item =>
item.category == "Misc" ||
item.category == "Resources" ||
item.category == "Fish" ||
((item as any).productCategory == "Pistols" && (item as MinWeapon).totalDamage == 0)
)
),
mods, mods,
badItems badItems
}); });

View File

@ -45,7 +45,8 @@ import {
IOperatorConfigDatabase, IOperatorConfigDatabase,
IPolarity, IPolarity,
IEquipmentDatabase, IEquipmentDatabase,
IOperatorConfigClient IOperatorConfigClient,
IArchonCrystalUpgrade
} from "@/src/types/inventoryTypes/commonInventoryTypes"; } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers"; import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
@ -182,6 +183,20 @@ ItemConfigSchema.set("toJSON", {
} }
}); });
const ArchonCrystalUpgradeSchema = new Schema<IArchonCrystalUpgrade>(
{
UpgradeType: String,
Color: String
},
{ _id: false }
);
ArchonCrystalUpgradeSchema.set("toJSON", {
transform(_document, returnedObject) {
delete returnedObject.__v;
}
});
const EquipmentSchema = new Schema<IEquipmentDatabase>({ const EquipmentSchema = new Schema<IEquipmentDatabase>({
ItemType: String, ItemType: String,
Configs: [ItemConfigSchema], Configs: [ItemConfigSchema],
@ -193,7 +208,7 @@ const EquipmentSchema = new Schema<IEquipmentDatabase>({
FocusLens: String, FocusLens: String,
ModSlotPurchases: Number, ModSlotPurchases: Number,
CustomizationSlotPurchases: Number, CustomizationSlotPurchases: Number,
UpgradeType: Schema.Types.Mixed, //todo UpgradeType: String,
UpgradeFingerprint: String, UpgradeFingerprint: String,
ItemName: String, ItemName: String,
InfestationDate: Date, InfestationDate: Date,
@ -203,7 +218,7 @@ const EquipmentSchema = new Schema<IEquipmentDatabase>({
UnlockLevel: Number, UnlockLevel: Number,
Expiry: Date, Expiry: Date,
SkillTree: String, SkillTree: String,
ArchonCrystalUpgrades: [Schema.Types.Mixed] //TODO ArchonCrystalUpgrades: { type: [ArchonCrystalUpgradeSchema], default: undefined }
}); });
EquipmentSchema.virtual("ItemId").get(function () { EquipmentSchema.virtual("ItemId").get(function () {
@ -432,16 +447,19 @@ const consumedSchuitsSchema = new Schema<IConsumedSuit>({
c: colorSchema c: colorSchema
}); });
const infestedFoundrySchema = new Schema<IInfestedFoundry>({ const infestedFoundrySchema = new Schema<IInfestedFoundry>(
{
Name: String, Name: String,
Resources: [typeCountSchema], Resources: { type: [typeCountSchema], default: undefined },
Slots: Number, Slots: Number,
XP: Number, XP: Number,
ConsumedSuits: [consumedSchuitsSchema], ConsumedSuits: { type: [consumedSchuitsSchema], default: undefined },
InvigorationIndex: Number, InvigorationIndex: Number,
InvigorationSuitOfferings: [String], InvigorationSuitOfferings: { type: [String], default: undefined },
InvigorationsApplied: Number InvigorationsApplied: Number
}); },
{ _id: false }
);
const questProgressSchema = new Schema<IQuestProgress>({ const questProgressSchema = new Schema<IQuestProgress>({
c: Number, c: Number,
@ -921,6 +939,7 @@ type InventoryDocumentProps = {
LongGuns: Types.DocumentArray<IEquipmentDatabase>; LongGuns: Types.DocumentArray<IEquipmentDatabase>;
Pistols: Types.DocumentArray<IEquipmentDatabase>; Pistols: Types.DocumentArray<IEquipmentDatabase>;
Melee: Types.DocumentArray<IEquipmentDatabase>; Melee: Types.DocumentArray<IEquipmentDatabase>;
OperatorAmps: Types.DocumentArray<IEquipmentDatabase>;
FlavourItems: Types.DocumentArray<IFlavourItem>; FlavourItems: Types.DocumentArray<IFlavourItem>;
RawUpgrades: Types.DocumentArray<IRawUpgrade>; RawUpgrades: Types.DocumentArray<IRawUpgrade>;
Upgrades: Types.DocumentArray<ICrewShipSalvagedWeaponSkin>; Upgrades: Types.DocumentArray<ICrewShipSalvagedWeaponSkin>;

View File

@ -1,126 +1,129 @@
import express from "express";
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
import { artifactsController } from "../controllers/api/artifactsController";
import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController"; import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
import { createGuildController } from "@/src/controllers/api/createGuildController";
import { deleteSessionController } from "@/src/controllers/api/deleteSessionController"; import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
import { dojoController } from "@/src/controllers/api/dojoController";
import { dronesController } from "@/src/controllers/api/dronesController"; import { dronesController } from "@/src/controllers/api/dronesController";
import { findSessionsController } from "@/src/controllers/api/findSessionsController"; import { findSessionsController } from "@/src/controllers/api/findSessionsController";
import { focusController } from "@/src/controllers/api/focusController";
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController"; import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
import { getAllianceController } from "@/src/controllers/api/getAllianceController"; import { getAllianceController } from "@/src/controllers/api/getAllianceController";
import { getCreditsController } from "@/src/controllers/api/getCreditsController"; import { getCreditsController } from "@/src/controllers/api/getCreditsController";
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
import { getFriendsController } from "@/src/controllers/api/getFriendsController"; import { getFriendsController } from "@/src/controllers/api/getFriendsController";
import { getGuildController } from "@/src/controllers/api/getGuildController";
import { getGuildDojoController } from "@/src/controllers/api/getGuildDojoController";
import { getGuildLogController } from "../controllers/api/getGuildLogController";
import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsersController"; import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsersController";
import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController"; import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController";
import { getShipController } from "@/src/controllers/api/getShipController"; import { getShipController } from "@/src/controllers/api/getShipController";
import { guildTechController } from "../controllers/api/guildTechController";
import { hostSessionController } from "@/src/controllers/api/hostSessionController"; import { hostSessionController } from "@/src/controllers/api/hostSessionController";
import { hubController } from "@/src/controllers/api/hubController"; import { hubController } from "@/src/controllers/api/hubController";
import { hubInstancesController } from "@/src/controllers/api/hubInstancesController"; import { hubInstancesController } from "@/src/controllers/api/hubInstancesController";
import { inboxController } from "@/src/controllers/api/inboxController"; import { inboxController } from "@/src/controllers/api/inboxController";
import { infestedFoundryController } from "@/src/controllers/api/infestedFoundryController";
import { inventoryController } from "@/src/controllers/api/inventoryController"; import { inventoryController } from "@/src/controllers/api/inventoryController";
import { inventorySlotsController } from "@/src/controllers/api/inventorySlotsController";
import { joinSessionController } from "@/src/controllers/api/joinSessionController";
import { loginController } from "@/src/controllers/api/loginController"; import { loginController } from "@/src/controllers/api/loginController";
import { loginRewardsController } from "@/src/controllers/api/loginRewardsController"; import { loginRewardsController } from "@/src/controllers/api/loginRewardsController";
import { logoutController } from "@/src/controllers/api/logoutController"; import { logoutController } from "@/src/controllers/api/logoutController";
import { marketRecommendationsController } from "@/src/controllers/api/marketRecommendationsController"; import { marketRecommendationsController } from "@/src/controllers/api/marketRecommendationsController";
import { missionInventoryUpdateController } from "@/src/controllers/api/missionInventoryUpdateController"; import { missionInventoryUpdateController } from "@/src/controllers/api/missionInventoryUpdateController";
import { modularWeaponCraftingController } from "@/src/controllers/api/modularWeaponCraftingController";
import { modularWeaponSaleController } from "@/src/controllers/api/modularWeaponSaleController"; import { modularWeaponSaleController } from "@/src/controllers/api/modularWeaponSaleController";
import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
import { purchaseController } from "@/src/controllers/api/purchaseController"; import { purchaseController } from "@/src/controllers/api/purchaseController";
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController"; import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
import { saveLoadoutController } from "@/src/controllers/api/saveLoadout";
import { sellController } from "@/src/controllers/api/sellController";
import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestController"; import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestController";
import { setActiveShipController } from "@/src/controllers/api/setActiveShipController";
import { setBootLocationController } from "@/src/controllers/api/setBootLocationController";
import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController";
import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController";
import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController";
import { startDojoRecipeController } from "@/src/controllers/api/startDojoRecipeController";
import { startRecipeController } from "@/src/controllers/api/startRecipeController";
import { stepSequencersController } from "@/src/controllers/api/stepSequencersController";
import { surveysController } from "@/src/controllers/api/surveysController"; import { surveysController } from "@/src/controllers/api/surveysController";
import { syndicateSacrificeController } from "../controllers/api/syndicateSacrificeController";
import { trainingResultController } from "@/src/controllers/api/trainingResultController";
import { updateChallengeProgressController } from "@/src/controllers/api/updateChallengeProgressController"; import { updateChallengeProgressController } from "@/src/controllers/api/updateChallengeProgressController";
import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController"; import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
import { joinSessionController } from "@/src/controllers/api/joinSessionController";
import { saveLoadoutController } from "@/src/controllers/api/saveLoadout";
import { trainingResultController } from "@/src/controllers/api/trainingResultController";
import { artifactsController } from "../controllers/api/artifactsController";
import express from "express";
import { setBootLocationController } from "@/src/controllers/api/setBootLocationController";
import { focusController } from "@/src/controllers/api/focusController";
import { inventorySlotsController } from "@/src/controllers/api/inventorySlotsController";
import { startRecipeController } from "@/src/controllers/api/startRecipeController";
import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController";
import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController";
import { setActiveShipController } from "@/src/controllers/api/setActiveShipController";
import { updateThemeController } from "../controllers/api/updateThemeController"; import { updateThemeController } from "../controllers/api/updateThemeController";
import { getGuildController } from "@/src/controllers/api/getGuildController";
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
import { createGuildController } from "@/src/controllers/api/createGuildController";
import { sellController } from "@/src/controllers/api/sellController";
import { upgradesController } from "@/src/controllers/api/upgradesController"; import { upgradesController } from "@/src/controllers/api/upgradesController";
import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController";
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
import { getGuildLogController } from "../controllers/api/getGuildLogController";
import { guildTechController } from "../controllers/api/guildTechController";
import { dojoController } from "@/src/controllers/api/dojoController";
import { getGuildDojoController } from "@/src/controllers/api/getGuildDojoController";
import { syndicateSacrificeController } from "../controllers/api/syndicateSacrificeController";
import { startDojoRecipeController } from "@/src/controllers/api/startDojoRecipeController";
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
const apiRouter = express.Router(); const apiRouter = express.Router();
// get // get
apiRouter.get("/inventory.php", inventoryController);
apiRouter.get("/getFriends.php", getFriendsController);
apiRouter.get("/marketRecommendations.php", marketRecommendationsController);
apiRouter.get("/marketSearchRecommendations.php", marketRecommendationsController);
apiRouter.get("/surveys.php", surveysController);
apiRouter.get("/loginRewards.php", loginRewardsController);
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController); apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
apiRouter.get("/inbox.php", inboxController); apiRouter.get("/credits.php", getCreditsController);
apiRouter.get("/getShip.php", getShipController); apiRouter.get("/deleteSession.php", deleteSessionController);
apiRouter.get("/dojo", dojoController);
apiRouter.get("/drones.php", dronesController); apiRouter.get("/drones.php", dronesController);
apiRouter.get("/getDailyDealStockLevels.php", getDailyDealStockLevelsController);
apiRouter.get("/getFriends.php", getFriendsController);
apiRouter.get("/getGuild.php", getGuildController);
apiRouter.get("/getGuildDojo.php", getGuildDojoController);
apiRouter.get("/getGuildLog.php", getGuildLogController);
apiRouter.get("/getIgnoredUsers.php", getIgnoredUsersController); apiRouter.get("/getIgnoredUsers.php", getIgnoredUsersController);
apiRouter.get("/getNewRewardSeed.php", getNewRewardSeedController); apiRouter.get("/getNewRewardSeed.php", getNewRewardSeedController);
apiRouter.get("/setActiveQuest.php", setActiveQuestController); apiRouter.get("/getShip.php", getShipController);
apiRouter.get("/updateSession.php", updateSessionGetController);
apiRouter.get("/credits.php", getCreditsController);
apiRouter.get("/hubInstances", hubInstancesController);
apiRouter.get("/hub", hubController); apiRouter.get("/hub", hubController);
apiRouter.get("/modularWeaponSale.php", modularWeaponSaleController); apiRouter.get("/hubInstances", hubInstancesController);
apiRouter.get("/deleteSession.php", deleteSessionController); apiRouter.get("/inbox.php", inboxController);
apiRouter.get("/inventory.php", inventoryController);
apiRouter.get("/loginRewards.php", loginRewardsController);
apiRouter.get("/logout.php", logoutController); apiRouter.get("/logout.php", logoutController);
apiRouter.get("/setBootLocation.php", setBootLocationController); apiRouter.get("/marketRecommendations.php", marketRecommendationsController);
apiRouter.get("/setActiveShip.php", setActiveShipController); apiRouter.get("/marketSearchRecommendations.php", marketRecommendationsController);
apiRouter.get("/getGuild.php", getGuildController); apiRouter.get("/modularWeaponSale.php", modularWeaponSaleController);
apiRouter.get("/setSupportedSyndicate.php", setSupportedSyndicateController);
apiRouter.get("/getDailyDealStockLevels.php", getDailyDealStockLevelsController);
apiRouter.get("/getGuildLog.php", getGuildLogController);
apiRouter.get("/dojo", dojoController);
apiRouter.get("/getGuildDojo.php", getGuildDojoController);
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController); apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
apiRouter.get("/setActiveShip.php", setActiveShipController);
apiRouter.get("/setBootLocation.php", setBootLocationController);
apiRouter.get("/setSupportedSyndicate.php", setSupportedSyndicateController);
apiRouter.get("/surveys.php", surveysController);
apiRouter.get("/updateSession.php", updateSessionGetController);
// post // post
// eslint-disable-next-line @typescript-eslint/no-misused-promises
apiRouter.post("/shipDecorations.php", shipDecorationsController);
apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController);
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
apiRouter.post("/startRecipe.php", startRecipeController);
apiRouter.post("/inventorySlots.php", inventorySlotsController);
apiRouter.post("/focus.php", focusController);
apiRouter.post("/artifacts.php", artifactsController);
apiRouter.post("/findSessions.php", findSessionsController);
// eslint-disable-next-line @typescript-eslint/no-misused-promises
apiRouter.post("/purchase.php", purchaseController);
apiRouter.post("/login.php", loginController);
apiRouter.post("/getAlliance.php", getAllianceController);
apiRouter.post("/updateChallengeProgress.php", updateChallengeProgressController);
apiRouter.post("/hostSession.php", hostSessionController);
apiRouter.post("/updateSession.php", updateSessionPostController);
apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController);
apiRouter.post("/genericUpdate.php", genericUpdateController);
apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
apiRouter.post("/joinSession.php", joinSessionController);
apiRouter.post("/saveLoadout.php", saveLoadoutController);
apiRouter.post("/trainingResult.php", trainingResultController);
apiRouter.post("/updateTheme.php", updateThemeController);
apiRouter.post("/addFriendImage.php", addFriendImageController); apiRouter.post("/addFriendImage.php", addFriendImageController);
apiRouter.post("/artifacts.php", artifactsController);
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
apiRouter.post("/createGuild.php", createGuildController); apiRouter.post("/createGuild.php", createGuildController);
apiRouter.post("/sell.php", sellController); apiRouter.post("/findSessions.php", findSessionsController);
apiRouter.post("/upgrades.php", upgradesController); apiRouter.post("/focus.php", focusController);
apiRouter.post("/genericUpdate.php", genericUpdateController);
apiRouter.post("/getAlliance.php", getAllianceController);
apiRouter.post("/guildTech.php", guildTechController); apiRouter.post("/guildTech.php", guildTechController);
apiRouter.post("/syndicateSacrifice.php", syndicateSacrificeController); apiRouter.post("/hostSession.php", hostSessionController);
apiRouter.post("/startDojoRecipe.php", startDojoRecipeController); apiRouter.post("/infestedFoundry.php", infestedFoundryController);
apiRouter.post("/inventorySlots.php", inventorySlotsController);
apiRouter.post("/joinSession.php", joinSessionController);
apiRouter.post("/login.php", loginController);
apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController);
apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController);
apiRouter.post("/nameWeapon.php", nameWeaponController); apiRouter.post("/nameWeapon.php", nameWeaponController);
apiRouter.post("/purchase.php", purchaseController);
apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
apiRouter.post("/saveLoadout.php", saveLoadoutController);
apiRouter.post("/sell.php", sellController);
apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController);
apiRouter.post("/shipDecorations.php", shipDecorationsController);
apiRouter.post("/startDojoRecipe.php", startDojoRecipeController);
apiRouter.post("/startRecipe.php", startRecipeController);
apiRouter.post("/stepSequencers.php", stepSequencersController);
apiRouter.post("/syndicateSacrifice.php", syndicateSacrificeController);
apiRouter.post("/trainingResult.php", trainingResultController);
apiRouter.post("/updateChallengeProgress.php", updateChallengeProgressController);
apiRouter.post("/updateSession.php", updateSessionPostController);
apiRouter.post("/updateTheme.php", updateThemeController);
apiRouter.post("/upgrades.php", upgradesController);
export { apiRouter }; export { apiRouter };

View File

@ -24,6 +24,7 @@ interface IConfig {
myAddress: string; myAddress: string;
httpPort?: number; httpPort?: number;
httpsPort?: number; httpsPort?: number;
myIrcAddresses?: string[];
autoCreateAccount?: boolean; autoCreateAccount?: boolean;
skipStoryModeChoice?: boolean; skipStoryModeChoice?: boolean;
skipTutorial?: boolean; skipTutorial?: boolean;

View File

@ -26,6 +26,7 @@ import { logger } from "@/src/utils/logger";
import { WeaponTypeInternal, getWeaponType, getExalted } from "@/src/services/itemDataService"; import { WeaponTypeInternal, getWeaponType, getExalted } from "@/src/services/itemDataService";
import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes"; import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes";
import { IEquipmentClient } from "../types/inventoryTypes/commonInventoryTypes"; import { IEquipmentClient } from "../types/inventoryTypes/commonInventoryTypes";
import { ExportRecipes } from "warframe-public-export-plus";
export const createInventory = async ( export const createInventory = async (
accountOwnerId: Types.ObjectId, accountOwnerId: Types.ObjectId,
@ -70,6 +71,25 @@ export const addItem = async (
typeName: string, typeName: string,
quantity: number = 1 quantity: number = 1
): Promise<{ InventoryChanges: object }> => { ): Promise<{ InventoryChanges: object }> => {
// Strict typing
if (typeName in ExportRecipes) {
const inventory = await getInventory(accountId);
const recipeChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies ITypeCount
];
addRecipes(inventory, recipeChanges);
await inventory.save();
return {
InventoryChanges: {
Recipes: recipeChanges
}
};
}
// Path-based duck typing
switch (typeName.substr(1).split("/")[1]) { switch (typeName.substr(1).split("/")[1]) {
case "Powersuits": case "Powersuits":
if (typeName.includes("EntratiMech")) { if (typeName.includes("EntratiMech")) {
@ -187,24 +207,6 @@ export const addItem = async (
} }
} }
} }
case "Recipes":
case "Consumables": {
// Blueprints for Ciphers, Antitoxins
const inventory = await getInventory(accountId);
const recipeChanges = [
{
ItemType: typeName,
ItemCount: quantity
} satisfies ITypeCount
];
addRecipes(inventory, recipeChanges);
await inventory.save();
return {
InventoryChanges: {
Recipes: recipeChanges
}
};
}
case "Restoratives": // Codex Scanner, Remote Observer, Starburst case "Restoratives": // Codex Scanner, Remote Observer, Starburst
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
const consumablesChanges = [ const consumablesChanges = [
@ -384,20 +386,23 @@ export const syndicateSacrifice = async (
export const addWeapon = async ( export const addWeapon = async (
weaponType: WeaponTypeInternal, weaponType: WeaponTypeInternal,
weaponName: string, weaponName: string,
accountId: string accountId: string,
modularParts: string[] | undefined = undefined
): Promise<IEquipmentClient> => { ): Promise<IEquipmentClient> => {
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
let weaponIndex; let weaponIndex;
switch (weaponType) { switch (weaponType) {
case "LongGuns": case "LongGuns":
weaponIndex = inventory.LongGuns.push({ ItemType: weaponName, Configs: [], XP: 0 });
break;
case "Pistols": case "Pistols":
weaponIndex = inventory.Pistols.push({ ItemType: weaponName, Configs: [], XP: 0 });
break;
case "Melee": case "Melee":
weaponIndex = inventory.Melee.push({ ItemType: weaponName, Configs: [], XP: 0 }); case "OperatorAmps":
weaponIndex = inventory[weaponType].push({
ItemType: weaponName,
Configs: [],
XP: 0,
ModularParts: modularParts
});
break; break;
default: default:
throw new Error("unknown weapon type: " + weaponType); throw new Error("unknown weapon type: " + weaponType);

View File

@ -1,23 +1,20 @@
import { getIndexAfter } from "@/src/helpers/stringHelpers"; import { getIndexAfter } from "@/src/helpers/stringHelpers";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import Items, { Buildable, Category, MinimalItem, Warframe, Weapon } from "warframe-items"; import Items, { Category, MinimalItem, Warframe, Weapon } from "warframe-items";
import badItems from "@/static/json/exclude-mods.json"; import badItems from "@/static/json/exclude-mods.json";
import { dict_en, ExportWarframes, ExportWeapons, IPowersuit } from "warframe-public-export-plus"; import {
dict_en,
ExportRecipes,
ExportWarframes,
ExportWeapons,
IPowersuit,
IRecipe
} from "warframe-public-export-plus";
export type MinWarframe = Omit<Warframe, "patchlogs">; export type MinWarframe = Omit<Warframe, "patchlogs">;
export type MinWeapon = Omit<Weapon, "patchlogs">; export type MinWeapon = Omit<Weapon, "patchlogs">;
export type MinItem = Omit<MinimalItem, "patchlogs">; export type MinItem = Omit<MinimalItem, "patchlogs">;
export const warframes: MinWarframe[] = Array.from(new Items({ category: ["Warframes"] }) as Warframe[])
.filter(item => {
return item.uniqueName.substring(0, 30) != "/Lotus/Powersuits/EntratiMech/";
})
.map(item => {
const next = { ...item };
delete next.patchlogs;
return next;
});
export type WeaponTypeInternal = export type WeaponTypeInternal =
| "LongGuns" | "LongGuns"
| "Pistols" | "Pistols"
@ -100,13 +97,8 @@ export const blueprintNames = Object.fromEntries(
.map(name => [name, craftNames[name]]) .map(name => [name, craftNames[name]])
); );
const buildables = items.filter(item => !!(item as Buildable).components); export const getRecipe = (uniqueName: string): IRecipe | undefined => {
return ExportRecipes[uniqueName];
export const getItemByBlueprint = (uniqueName: string): (MinItem & Buildable) | undefined => {
const item = buildables.find(item =>
(item as Buildable).components?.find(component => component.uniqueName === uniqueName)
);
return item;
}; };
export const getExalted = (uniqueName: string) => { export const getExalted = (uniqueName: string) => {

View File

@ -3,7 +3,7 @@ import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers";
import { addItem, addBooster, updateCurrency, updateSlots } from "@/src/services/inventoryService"; import { addItem, addBooster, updateCurrency, updateSlots } from "@/src/services/inventoryService";
import { IPurchaseRequest, SlotPurchase } from "@/src/types/purchaseTypes"; import { IPurchaseRequest, SlotPurchase } from "@/src/types/purchaseTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { ExportBundles } from "warframe-public-export-plus"; import { ExportBundles, TRarity } from "warframe-public-export-plus";
export const getStoreItemCategory = (storeItem: string) => { export const getStoreItemCategory = (storeItem: string) => {
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/"); const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
@ -26,7 +26,8 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI
const purchaseResponse = await handleStoreItemAcquisition( const purchaseResponse = await handleStoreItemAcquisition(
purchaseRequest.PurchaseParams.StoreItem, purchaseRequest.PurchaseParams.StoreItem,
accountId, accountId,
purchaseRequest.PurchaseParams.Quantity purchaseRequest.PurchaseParams.Quantity,
"COMMON"
); );
if (!purchaseResponse) throw new Error("purchase response was undefined"); if (!purchaseResponse) throw new Error("purchase response was undefined");
@ -48,7 +49,8 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI
const handleStoreItemAcquisition = async ( const handleStoreItemAcquisition = async (
storeItemName: string, storeItemName: string,
accountId: string, accountId: string,
quantity: number quantity: number,
durability: TRarity
): Promise<{ InventoryChanges: object }> => { ): Promise<{ InventoryChanges: object }> => {
let purchaseResponse = { let purchaseResponse = {
InventoryChanges: {} InventoryChanges: {}
@ -60,7 +62,12 @@ const handleStoreItemAcquisition = async (
for (const component of bundle.components) { for (const component of bundle.components) {
purchaseResponse = { purchaseResponse = {
...purchaseResponse, ...purchaseResponse,
...(await handleStoreItemAcquisition(component.typeName, accountId, component.purchaseQuantity)) ...(await handleStoreItemAcquisition(
component.typeName,
accountId,
component.purchaseQuantity,
component.durability
))
}; };
} }
} else { } else {
@ -75,7 +82,7 @@ const handleStoreItemAcquisition = async (
purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity); purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity);
break; break;
case "Boosters": case "Boosters":
purchaseResponse = await handleBoostersPurchase(internalName, accountId); purchaseResponse = await handleBoostersPurchase(internalName, accountId, durability);
break; break;
} }
} }
@ -144,17 +151,21 @@ const boosterCollection = [
"/Lotus/Types/Boosters/CreditBooster" "/Lotus/Types/Boosters/CreditBooster"
]; ];
const handleBoostersPurchase = async (boosterStoreName: string, accountId: string) => { const boosterDuration: Record<TRarity, number> = {
const match = boosterStoreName.match(/(\d+)Day/); COMMON: 3 * 86400,
if (!match) { UNCOMMON: 7 * 86400,
RARE: 30 * 86400,
LEGENDARY: 90 * 86400
};
const handleBoostersPurchase = async (boosterStoreName: string, accountId: string, durability: TRarity) => {
const ItemType = boosterStoreName.replace("StoreItem", "");
if (!boosterCollection.find(x => x == ItemType)) {
logger.error(`unknown booster type: ${ItemType}`);
return { InventoryChanges: {} }; return { InventoryChanges: {} };
} }
const extractedDigit = Number(match[1]); const ExpiryDate = boosterDuration[durability];
const ItemType = boosterCollection.find(i =>
boosterStoreName.includes(i.split("/").pop()!.replace("Booster", ""))
)!;
const ExpiryDate = extractedDigit * 86400;
await addBooster(ItemType, ExpiryDate, accountId); await addBooster(ItemType, ExpiryDate, accountId);

View File

@ -1,65 +1,30 @@
import { unixTimesInMs } from "@/src/constants/timeConstants"; import { unixTimesInMs } from "@/src/constants/timeConstants";
import { getInventory } from "@/src/services/inventoryService"; import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getItemByBlueprint } from "@/src/services/itemDataService"; import { getRecipe } from "@/src/services/itemDataService";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { Types } from "mongoose"; import { Types } from "mongoose";
export interface IResource {
uniqueName: string;
count: number;
}
// export const updateResources = async (accountId: string, components: IResource[]) => {
// const inventory = await getInventory(accountId);
// for (const component of components) {
// const category = getItemCategoryByUniqueName(component.uniqueName) as keyof typeof inventory;
// //validate category
// console.log(component.uniqueName);
// console.log("cate", category);
// const invItem = inventory[category];
// console.log("invItem", invItem);
// inventory["MiscItems"];
// }
// };
export const startRecipe = async (recipeName: string, accountId: string) => { export const startRecipe = async (recipeName: string, accountId: string) => {
const recipe = getItemByBlueprint(recipeName); const recipe = getRecipe(recipeName);
if (!recipe) { if (!recipe) {
logger.error(`unknown recipe ${recipeName}`); logger.error(`unknown recipe ${recipeName}`);
throw new Error(`unknown recipe ${recipeName}`); throw new Error(`unknown recipe ${recipeName}`);
} }
const componentsNeeded = recipe.components?.map(component => ({ await updateCurrency(recipe.buildPrice, false, accountId);
uniqueName: component.uniqueName,
count: component.itemCount const ingredientsInverse = recipe.ingredients.map(component => ({
ItemType: component.ItemType,
ItemCount: component.ItemCount * -1
})); }));
if (!componentsNeeded) { const inventory = await getInventory(accountId);
logger.error(`recipe ${recipeName} has no components`); addMiscItems(inventory, ingredientsInverse);
throw new Error(`recipe ${recipeName} has no components`);
}
//TODO: consume components used
//await updateResources(accountId, componentsNeeded);
//might be redundant
if (recipe.consumeOnBuild) {
//consume
}
if (!recipe.buildTime) {
logger.error(`recipe ${recipeName} has no build time`);
throw new Error(`recipe ${recipeName} has no build time`);
}
//buildtime is in seconds //buildtime is in seconds
const completionDate = new Date(Date.now() + recipe.buildTime * unixTimesInMs.second); const completionDate = new Date(Date.now() + recipe.buildTime * unixTimesInMs.second);
const inventory = await getInventory(accountId);
inventory.PendingRecipes.push({ inventory.PendingRecipes.push({
ItemType: recipeName, ItemType: recipeName,
CompletionDate: completionDate, CompletionDate: completionDate,

View File

@ -103,6 +103,11 @@ export interface IEquipmentDatabase {
UnlockLevel?: number; UnlockLevel?: number;
Expiry?: IMongoDate; Expiry?: IMongoDate;
SkillTree?: string; SkillTree?: string;
ArchonCrystalUpgrades?: []; //TODO ArchonCrystalUpgrades?: IArchonCrystalUpgrade[];
_id: Types.ObjectId; _id: Types.ObjectId;
} }
export interface IArchonCrystalUpgrade {
UpgradeType?: string;
Color?: string;
}

View File

@ -232,7 +232,7 @@ export interface IInventoryResponse {
DailyAffiliationEntrati: number; DailyAffiliationEntrati: number;
DailyAffiliationNecraloid: number; DailyAffiliationNecraloid: number;
MechSuits: IEquipmentDatabase[]; MechSuits: IEquipmentDatabase[];
InfestedFoundry: IInfestedFoundry; InfestedFoundry?: IInfestedFoundry;
BlessingCooldown: IMongoDate; BlessingCooldown: IMongoDate;
CrewShipHarnesses: IEquipmentDatabase[]; CrewShipHarnesses: IEquipmentDatabase[];
CrewShipRawSalvage: IConsumable[]; CrewShipRawSalvage: IConsumable[];
@ -482,14 +482,14 @@ export interface IFusionTreasure {
} }
export interface IInfestedFoundry { export interface IInfestedFoundry {
Name: string; Name?: string;
Resources: ITypeCount[]; Resources?: ITypeCount[];
Slots: number; Slots?: number;
XP: number; XP?: number;
ConsumedSuits: IConsumedSuit[]; ConsumedSuits?: IConsumedSuit[];
InvigorationIndex: number; InvigorationIndex?: number;
InvigorationSuitOfferings: string[]; InvigorationSuitOfferings?: string[];
InvigorationsApplied: number; InvigorationsApplied?: number;
} }
export interface IConsumedSuit { export interface IConsumedSuit {
@ -831,7 +831,7 @@ export interface IStepSequencer {
NotePacks: INotePacks; NotePacks: INotePacks;
FingerPrint: string; FingerPrint: string;
Name: string; Name: string;
ItemId: IOid; ItemId?: IOid;
} }
export interface INotePacks { export interface INotePacks {

File diff suppressed because it is too large Load Diff

View File

@ -1249,6 +1249,11 @@
"Tier": 1, "Tier": 1,
"Tag": "SolNode721" "Tag": "SolNode721"
}, },
{
"Completes": 1,
"Tier": 1,
"Tag": "SolNode723"
},
{ {
"Completes": 1, "Completes": 1,
"Tier": 1, "Tier": 1,
@ -1516,6 +1521,7 @@
}, },
{ {
"Completes": 1, "Completes": 1,
"Tier": 1,
"Tag": "UranusToNeptuneJunction" "Tag": "UranusToNeptuneJunction"
}, },
{ {

View File

@ -1,38 +0,0 @@
[
"/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain",
"/Lotus/Types/Keys/GlassQuest/GlassQuestKeyChain",
"/Lotus/Types/Keys/SolarisQuest/SolarisQuestKeyChain",
"/Lotus/Types/Keys/InfestedIntroQuest/InfestedIntroQuestKeyChain",
"/Lotus/Types/Keys/KubrowQuest/KubrowQuestKeyChain",
"/Lotus/Types/Keys/ArchwingQuest/ArchwingQuestKeyChain",
"/Lotus/Types/Keys/GetClemQuest/GetClemQuestKeyChain",
"/Lotus/Types/Keys/SpyQuestKeyChain/SpyQuestKeyChain",
"/Lotus/Types/Keys/DragonQuest/DragonQuestKeyChain",
"/Lotus/Types/Keys/LimboQuest/LimboQuestKeyChain",
"/Lotus/Types/Keys/SentientQuest/SentientQuestKeyChain",
"/Lotus/Types/Keys/OrokinMoonQuest/OrokinMoonQuestKeyChain",
"/Lotus/Types/Keys/MirageQuest/MirageQuestKeyChain",
"/Lotus/Types/Keys/WarWithinQuest/WarWithinQuestKeyChain",
"/Lotus/Types/Keys/InfestedAladVQuest/InfestedAladVQuestKeyChain",
"/Lotus/Types/Keys/GolemQuest/GolemQuestKeyChainItem",
"/Lotus/Types/Keys/BardQuest/BardQuestKeyChain",
"/Lotus/Types/Keys/FairyQuest/FairyQuestKeyChain",
"/Lotus/Types/Keys/IndexQuest/IndexQuestKeyChain",
"/Lotus/Types/Keys/PriestFrameQuest/PriestQuestKeyChain",
"/Lotus/Types/Keys/ApostasyQuest/ApostasyKeyChain",
"/Lotus/Types/Keys/SacrificeQuest/SacrificeQuestKeyChain",
"/Lotus/Types/Keys/ChimeraQuest/ChimeraKeyChain",
"/Lotus/Types/Keys/MummyQuest/MummyQuestKeyChain",
"/Lotus/Types/Keys/RailJackBuildQuest/RailjackBuildQuestKeyChain",
"/Lotus/Types/Keys/NewWarIntroQuest/NewWarIntroKeyChain",
"/Lotus/Types/Keys/ProteaQuest/ProteaQuestKeyChain",
"/Lotus/Types/Keys/RevenantQuest/RevenantQuestKeyChain",
"/Lotus/Types/Keys/InfestedMicroplanetQuest/InfestedMicroplanetQuestKeyChain",
"/Lotus/Types/Keys/WraithQuest/WraithQuestKeyChain",
"/Lotus/Types/Keys/YareliQuest/YareliQuestKeyChain",
"/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain",
"/Lotus/Types/Keys/ZarimanQuest/ZarimanQuestKeyChain",
"/Lotus/Types/Keys/KahlQuest/KahlQuestKeyChain",
"/Lotus/Types/Keys/DuviriQuest/DuviriQuestKeyChain",
"/Lotus/Types/Keys/EntratiLab/EntratiQuestKeyChain"
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -96,6 +96,10 @@ window.itemListPromise = new Promise(resolve => {
"/Lotus/Weapons/Tenno/Rifle/LotusRifle": { name: "Rifle" }, "/Lotus/Weapons/Tenno/Rifle/LotusRifle": { name: "Rifle" },
"/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { name: "Shotgun" }, "/Lotus/Weapons/Tenno/Shotgun/LotusShotgun": { name: "Shotgun" },
// Modular weapons // Modular weapons
"/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam": { name: "Kitgun" },
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": { name: "Kitgun" },
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: "Kitgun" },
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: "Kitgun" },
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" }, "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" },
// Missing in data sources // Missing in data sources
"/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" } "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" }
@ -524,7 +528,7 @@ function doAcquireMiscItems() {
MiscItems: [ MiscItems: [
{ {
ItemType: uniqueName, ItemType: uniqueName,
ItemCount: $("#miscitem-count").val() ItemCount: parseInt($("#miscitem-count").val())
} }
] ]
}) })