This commit is contained in:
nrbdev 2025-01-05 14:52:12 -05:00
commit 8c46a5391b
38 changed files with 4506 additions and 316 deletions

8
package-lock.json generated
View File

@ -12,7 +12,7 @@
"copyfiles": "^2.4.1",
"express": "^5",
"mongoose": "^8.9.2",
"warframe-public-export-plus": "^0.5.18",
"warframe-public-export-plus": "^0.5.19",
"warframe-riven-info": "^0.1.2",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0"
@ -3778,9 +3778,9 @@
}
},
"node_modules/warframe-public-export-plus": {
"version": "0.5.18",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.18.tgz",
"integrity": "sha512-wUaW5Ua5tXHOYkKJxbealdCcTnRLUN7UCkvYOJEwlB/H14EBzDaqxg4engGqzbq4H8fmttyp3EUo4vazSaxZWg=="
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.19.tgz",
"integrity": "sha512-ERCPAe4ojJXts6tyNPBvNsFcgAwJuV3M04iDfXhudJfpJrg0qseDO4AExjSyFo+WUvKoWROMCy9dCRzxIbNATw=="
},
"node_modules/warframe-riven-info": {
"version": "0.1.2",

View File

@ -16,7 +16,7 @@
"copyfiles": "^2.4.1",
"express": "^5",
"mongoose": "^8.9.2",
"warframe-public-export-plus": "^0.5.18",
"warframe-public-export-plus": "^0.5.19",
"warframe-riven-info": "^0.1.2",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0"

View File

@ -1,16 +1,15 @@
import express from "express";
import bodyParser from "body-parser";
import { unknownEndpointHandler } from "@/src/middleware/middleware";
import { requestLogger } from "@/src/middleware/morgenMiddleware";
import { errorHandler } from "@/src/middleware/errorHandler";
import { apiRouter } from "@/src/routes/api";
//import { testRouter } from "@/src/routes/test";
import { cacheRouter } from "@/src/routes/cache";
import bodyParser from "body-parser";
import { steamPacksController } from "@/src/controllers/misc/steamPacksController";
import { customRouter } from "@/src/routes/custom";
import { dynamicController } from "@/src/routes/dynamic";
import { payRouter } from "@/src/routes/pay";
import { statsRouter } from "@/src/routes/stats";
import { webuiRouter } from "@/src/routes/webui";
@ -20,21 +19,16 @@ app.use(bodyParser.raw());
app.use(express.json());
app.use(bodyParser.text());
app.use(requestLogger);
//app.use(requestLogger);
app.use("/api", apiRouter);
//app.use("/test", testRouter);
app.use("/", cacheRouter);
app.use("/custom", customRouter);
app.use("/:id/dynamic", dynamicController);
app.post("/pay/steamPacks.php", steamPacksController);
app.use("/pay", payRouter);
app.use("/stats", statsRouter);
app.use("/", webuiRouter);
app.use(unknownEndpointHandler);
//app.use(errorHandler)
app.use(errorHandler);
export { app };

View File

@ -30,13 +30,11 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
recipe => recipe._id?.toString() === claimCompletedRecipeRequest.RecipeIds[0].$oid
);
if (!pendingRecipe) {
logger.error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`);
throw new Error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`);
}
//check recipe is indeed ready to be completed
// if (pendingRecipe.CompletionDate > new Date()) {
// logger.error(`recipe ${pendingRecipe._id} is not ready to be completed`);
// throw new Error(`recipe ${pendingRecipe._id} is not ready to be completed`);
// }
@ -45,7 +43,6 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
const recipe = getRecipe(pendingRecipe.ItemType);
if (!recipe) {
logger.error(`no completed item found for recipe ${pendingRecipe._id.toString()}`);
throw new Error(`no completed item found for recipe ${pendingRecipe._id.toString()}`);
}
@ -83,11 +80,12 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
...(await updateCurrencyByAccountId(recipe.skipBuildTimePrice, true, accountId))
};
}
res.json({
InventoryChanges: {
...InventoryChanges,
...(await addItem(accountId, recipe.resultType, recipe.num)).InventoryChanges
}
});
const inventory = await getInventory(accountId);
InventoryChanges = {
...InventoryChanges,
...(await addItem(inventory, recipe.resultType, recipe.num)).InventoryChanges
};
await inventory.save();
res.json({ InventoryChanges });
}
};

View File

@ -3,14 +3,8 @@ import { config } from "@/src/services/configService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
export const getCreditsController: RequestHandler = async (req, res) => {
let accountId;
try {
accountId = await getAccountIdForRequest(req);
} catch (e) {
res.status(400).send("Log-in expired");
return;
}
export const creditsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);

View File

@ -4,6 +4,7 @@ import { getInventory, addMiscItems, addEquipment } from "@/src/services/invento
import { IMiscItem, TFocusPolarity, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger";
import { ExportFocusUpgrades } from "warframe-public-export-plus";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
export const focusController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -102,8 +103,10 @@ export const focusController: RequestHandler = async (req, res) => {
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis",
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
];
const result = await addEquipment("OperatorAmps", request.StartingWeaponType, accountId, parts);
res.json(result);
const inventory = await getInventory(accountId);
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, parts);
await inventory.save();
res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]);
break;
}
case FocusOperation.UnbindUpgrade: {

View File

@ -5,7 +5,6 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { getPersonalRooms } from "@/src/services/personalRoomsService";
import { getShip } from "@/src/services/shipService";
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
import { logger } from "@/src/utils/logger";
import { toOid } from "@/src/helpers/inventoryHelpers";
import { IGetShipResponse } from "@/src/types/shipTypes";
import { IPersonalRooms } from "@/src/types/personalRoomsTypes";
@ -44,8 +43,7 @@ export const getLoadout = async (accountId: string) => {
const loadout = await Loadout.findOne({ loadoutOwnerId: accountId });
if (!loadout) {
logger.error(`loadout not found for account ${accountId}`);
throw new Error("loadout not found");
throw new Error(`loadout not found for account ${accountId}`);
}
return loadout;

View File

@ -2,7 +2,7 @@ import { RequestHandler } from "express";
import { getGuildForRequestEx } from "@/src/services/guildService";
import { ExportDojoRecipes } from "warframe-public-export-plus";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
@ -11,11 +11,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
const inventory = await getInventory(accountId);
const guild = await getGuildForRequestEx(req, inventory);
const data = JSON.parse(String(req.body)) as TGuildTechRequest;
if (data.Action == "Sync") {
const action = data.Action.split(",")[0];
if (action == "Sync") {
res.json({
TechProjects: guild.toJSON().TechProjects
});
} else if (data.Action == "Start") {
} else if (action == "Start") {
const recipe = ExportDojoRecipes.research[data.RecipeType!];
guild.TechProjects ??= [];
if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) {
@ -31,7 +32,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
}
await guild.save();
res.end();
} else if (data.Action == "Contribute") {
} else if (action == "Contribute") {
const contributions = data as IGuildTechContributeFields;
const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
if (contributions.RegularCredits > techProject.ReqCredits) {
@ -70,6 +71,30 @@ export const guildTechController: RequestHandler = async (req, res) => {
res.json({
InventoryChanges: inventoryChanges
});
} else if (action == "Buy") {
const purchase = data as IGuildTechBuyFields;
const quantity = parseInt(data.Action.split(",")[1]);
const inventory = await getInventory(accountId);
const recipeChanges = [
{
ItemType: purchase.RecipeType,
ItemCount: quantity
}
];
addRecipes(inventory, recipeChanges);
const currencyChanges = updateCurrency(
inventory,
ExportDojoRecipes.research[purchase.RecipeType].replicatePrice,
false
);
await inventory.save();
// Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
res.json({
inventoryChanges: {
...currencyChanges,
Recipes: recipeChanges
}
});
} else {
throw new Error(`unknown guildTech action: ${data.Action}`);
}
@ -85,6 +110,8 @@ interface IGuildTechStartFields {
RecipeType: string;
}
type IGuildTechBuyFields = IGuildTechStartFields;
interface IGuildTechContributeFields {
ResearchId: "";
RecipeType: string;

View File

@ -3,15 +3,12 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory, addMiscItems, updateCurrency, addRecipes } from "@/src/services/inventoryService";
import { IOid } from "@/src/types/commonTypes";
import {
IConsumedSuit,
IInfestedFoundry,
IInventoryDatabaseDocument,
IMiscItem,
ITypeCount
} from "@/src/types/inventoryTypes/inventoryTypes";
import { IConsumedSuit, IInfestedFoundry, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
import { ExportMisc, ExportRecipes } from "warframe-public-export-plus";
import { getRecipe } from "@/src/services/itemDataService";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { logger } from "@/src/utils/logger";
export const infestedFoundryController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
@ -44,6 +41,25 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
break;
}
case "x": {
// shard removal
const request = getJSONfromString(String(req.body)) as IShardUninstallRequest;
const inventory = await getInventory(accountId);
const suit = inventory.Suits.find(suit => suit._id.toString() == request.SuitId.$oid)!;
suit.ArchonCrystalUpgrades![request.Slot] = {};
const bile = inventory.InfestedFoundry!.Resources!.find(
x => x.ItemType == "/Lotus/Types/Items/InfestedFoundry/HelminthBile"
)!;
bile.Count -= 300;
await inventory.save();
res.json({
InventoryChanges: {
InfestedFoundry: inventory.toJSON().InfestedFoundry
}
});
break;
}
case "n": {
// name the beast
const request = getJSONfromString(String(req.body)) as IHelminthNameRequest;
@ -71,21 +87,43 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
const miscItemChanges: IMiscItem[] = [];
let totalPercentagePointsGained = 0;
const currentUnixSeconds = Math.trunc(new Date().getTime() / 1000);
for (const contribution of request.ResourceContributions) {
const snack = ExportMisc.helminthSnacks[contribution.ItemType];
// Note: Currently ignoring loss of apetite
totalPercentagePointsGained += snack.gain / 0.01;
const resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type);
if (resource) {
resource.Count += Math.trunc(snack.gain * 1000);
} else {
inventory.InfestedFoundry.Resources.push({
ItemType: snack.type,
Count: Math.trunc(snack.gain * 1000)
});
let resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type);
if (!resource) {
resource =
inventory.InfestedFoundry.Resources[
inventory.InfestedFoundry.Resources.push({ ItemType: snack.type, Count: 0 }) - 1
];
}
resource.RecentlyConvertedResources ??= [];
let record = resource.RecentlyConvertedResources.find(x => x.ItemType == contribution.ItemType);
if (!record) {
record =
resource.RecentlyConvertedResources[
resource.RecentlyConvertedResources.push({ ItemType: contribution.ItemType, Date: 0 }) - 1
];
}
const hoursRemaining = (record.Date - currentUnixSeconds) / 3600;
const apetiteFactor = apetiteModel(hoursRemaining) / 30;
logger.debug(`helminth eating ${contribution.ItemType} (+${(snack.gain * 100).toFixed(0)}%)`, {
hoursRemaining,
apetiteFactor
});
if (hoursRemaining >= 18) {
record.Date = currentUnixSeconds + 72 * 60 * 60;
} else {
record.Date = currentUnixSeconds + 24 * 60 * 60;
}
totalPercentagePointsGained += snack.gain * 100 * apetiteFactor; // 30% would be gain=0.3, so percentage points is equal to gain * 100.
resource.Count += Math.trunc(snack.gain * 1000 * apetiteFactor); // 30% would be gain=0.3 or Count=300, so Count=gain*1000.
// tally items for removal
const change = miscItemChanges.find(x => x.ItemType == contribution.ItemType);
if (change) {
@ -95,27 +133,43 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
}
}
addInfestedFoundryXP(inventory.InfestedFoundry, 666 * totalPercentagePointsGained);
const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry, 666 * totalPercentagePointsGained);
addRecipes(inventory, recipeChanges);
addMiscItems(inventory, miscItemChanges);
await inventory.save();
res.json({
InventoryChanges: {
Recipes: recipeChanges,
InfestedFoundry: {
XP: inventory.InfestedFoundry.XP,
Resources: inventory.InfestedFoundry.Resources,
Slots: inventory.InfestedFoundry.Slots
}
},
MiscItems: miscItemChanges
},
MiscItems: miscItemChanges
}
});
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();
case "o": {
// offerings update
const request = getJSONfromString(String(req.body)) as IHelminthOfferingsUpdate;
const inventory = await getInventory(accountId);
inventory.InfestedFoundry ??= {};
inventory.InfestedFoundry.InvigorationIndex = request.OfferingsIndex;
inventory.InfestedFoundry.InvigorationSuitOfferings = request.SuitTypes;
if (request.Extra) {
inventory.InfestedFoundry.InvigorationsApplied = 0;
}
await inventory.save();
res.json({
InventoryChanges: {
InfestedFoundry: inventory.toJSON().InfestedFoundry
}
});
break;
}
case "a": {
// subsume warframe
@ -134,18 +188,21 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
if (suit.Configs && suit.Configs[0] && suit.Configs[0].pricol) {
consumedSuit.c = suit.Configs[0].pricol;
}
inventory.InfestedFoundry!.Slots!--;
if ((inventory.InfestedFoundry!.XP ?? 0) < 73125_00) {
inventory.InfestedFoundry!.Slots!--;
}
inventory.InfestedFoundry!.ConsumedSuits ??= [];
inventory.InfestedFoundry!.ConsumedSuits?.push(consumedSuit);
inventory.InfestedFoundry!.LastConsumedSuit = suit;
inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = new Date(
new Date().getTime() + 24 * 60 * 60 * 1000
);
addInfestedFoundryXP(inventory.InfestedFoundry!, 1600_00);
const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 1600_00);
addRecipes(inventory, recipeChanges);
await inventory.save();
console.log(inventory.toJSON().InfestedFoundry);
res.json({
InventoryChanges: {
Recipes: recipeChanges,
RemovedIdItems: [
{
ItemId: request.SuitId
@ -178,6 +235,52 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
break;
}
case "u": {
const request = getJSONfromString(String(req.body)) as IHelminthInvigorationRequest;
const inventory = await getInventory(accountId);
const suit = inventory.Suits.find(x => x._id.toString() == request.SuitId.$oid)!;
const upgradesExpiry = new Date(new Date().getTime() + 7 * 24 * 60 * 60 * 1000);
suit.OffensiveUpgrade = request.OffensiveUpgradeType;
suit.DefensiveUpgrade = request.DefensiveUpgradeType;
suit.UpgradesExpiry = upgradesExpiry;
const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 4800_00);
addRecipes(inventory, recipeChanges);
for (let i = 0; i != request.ResourceTypes.length; ++i) {
inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == request.ResourceTypes[i])!.Count -=
request.ResourceCosts[i];
}
inventory.InfestedFoundry!.InvigorationsApplied ??= 0;
inventory.InfestedFoundry!.InvigorationsApplied += 1;
await inventory.save();
res.json({
SuitId: request.SuitId,
OffensiveUpgrade: request.OffensiveUpgradeType,
DefensiveUpgrade: request.DefensiveUpgradeType,
UpgradesExpiry: toMongoDate(upgradesExpiry),
InventoryChanges: {
Recipes: recipeChanges,
InfestedFoundry: inventory.toJSON().InfestedFoundry
}
});
break;
}
case "custom_unlockall": {
const inventory = await getInventory(accountId);
inventory.InfestedFoundry ??= {};
inventory.InfestedFoundry.XP ??= 0;
if (151875_00 > inventory.InfestedFoundry.XP) {
const recipeChanges = addInfestedFoundryXP(
inventory.InfestedFoundry,
151875_00 - inventory.InfestedFoundry.XP
);
addRecipes(inventory, recipeChanges);
await inventory.save();
}
res.end();
break;
}
default:
throw new Error(`unhandled infestedFoundry mode: ${String(req.query.mode)}`);
}
@ -190,6 +293,11 @@ interface IShardInstallRequest {
Color: string;
}
interface IShardUninstallRequest {
SuitId: IOid;
Slot: number;
}
interface IHelminthNameRequest {
newName: string;
}
@ -216,7 +324,8 @@ const colorToShard: Record<string, string> = {
ACC_PURPLE_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalVioletMythic"
};
const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number): void => {
export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number): ITypeCount[] => {
const recipeChanges: ITypeCount[] = [];
infestedFoundry.XP ??= 0;
const prevXP = infestedFoundry.XP;
infestedFoundry.XP += delta;
@ -224,14 +333,69 @@ const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number):
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 {
@ -239,7 +403,7 @@ interface IHelminthSubsumeRequest {
Recipe: string;
}
export const handleSubsumeCompletion = (inventory: IInventoryDatabaseDocument): ITypeCount[] => {
export const handleSubsumeCompletion = (inventory: TInventoryDatabaseDocument): ITypeCount[] => {
const [recipeType] = Object.entries(ExportRecipes).find(
([_recipeType, recipe]) =>
recipe.secretIngredientAction == "SIA_WARFRAME_ABILITY" &&
@ -256,3 +420,52 @@ export const handleSubsumeCompletion = (inventory: IInventoryDatabaseDocument):
addRecipes(inventory, recipeChanges);
return recipeChanges;
};
interface IHelminthOfferingsUpdate {
OfferingsIndex: number;
SuitTypes: string[];
Extra: boolean;
}
interface IHelminthInvigorationRequest {
SuitId: IOid;
OffensiveUpgradeType: string;
DefensiveUpgradeType: string;
ResourceTypes: string[];
ResourceCosts: number[];
}
// Hours remaining, percentage points gained (out of 30 total)
// 0, 30
// 5, 25.8
// 10, 21.6
// 12, 20
// 16, 16.6
// 17, 15.8
// 18, 15
// 20, 15
// 24, 15
// 36, 15
// 40, 13.6
// 47, 11.3
// 48, 11
// 50, 10.3
// 60, 7
// 70, 3.6
// 71, 3.3
// 72, 3
const apetiteModel = (x: number): number => {
if (x <= 0) {
return 30;
}
if (x < 18) {
return -0.84 * x + 30;
}
if (x <= 36) {
return 15;
}
if (x < 71.9) {
return -0.3327892 * x + 26.94135;
}
return 3;
};

View File

@ -1,11 +1,10 @@
import { RequestHandler } from "express";
import { getAccountForRequest } from "@/src/services/loginService";
import { toInventoryResponse } from "@/src/helpers/inventoryHelpers";
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { config } from "@/src/services/configService";
import allDialogue from "@/static/fixed_responses/allDialogue.json";
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
import { IInventoryDatabaseDocument, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryResponse, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
import { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
import {
ExportCustoms,
@ -18,17 +17,9 @@ import {
import { handleSubsumeCompletion } from "./infestedFoundryController";
export const inventoryController: RequestHandler = async (request, response) => {
let account;
try {
account = await getAccountForRequest(request);
} catch (e) {
response.status(400).send("Log-in expired");
return;
}
const account = await getAccountForRequest(request);
const inventory = await Inventory.findOne({ accountOwnerId: account._id.toString() })
.populate<{ LoadOutPresets: ILoadoutDatabase }>("LoadOutPresets")
.populate<{ Ships: IShipInventory }>("Ships");
const inventory = await Inventory.findOne({ accountOwnerId: account._id.toString() });
if (!inventory) {
response.status(400).json({ error: "inventory was undefined" });
@ -41,20 +32,21 @@ export const inventoryController: RequestHandler = async (request, response) =>
account.LastLoginDay = today;
await account.save();
inventory.DailyAffiliation = 16000;
inventory.DailyAffiliationPvp = 16000;
inventory.DailyAffiliationLibrary = 16000;
inventory.DailyAffiliationCetus = 16000;
inventory.DailyAffiliationQuills = 16000;
inventory.DailyAffiliationSolaris = 16000;
inventory.DailyAffiliationVentkids = 16000;
inventory.DailyAffiliationVox = 16000;
inventory.DailyAffiliationEntrati = 16000;
inventory.DailyAffiliationNecraloid = 16000;
inventory.DailyAffiliationZariman = 16000;
inventory.DailyAffiliationKahl = 16000;
inventory.DailyAffiliationCavia = 16000;
inventory.DailyAffiliationHex = 16000;
inventory.DailyAffiliation = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationPvp = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationLibrary = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationCetus = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationQuills = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationSolaris = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationVentkids = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationVox = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationEntrati = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationNecraloid = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationZariman = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationKahl = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationCavia = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationHex = 16000 + inventory.PlayerLevel * 500;
inventory.DailyFocus = 250000 + inventory.PlayerLevel * 5000;
await inventory.save();
}
@ -63,14 +55,17 @@ export const inventoryController: RequestHandler = async (request, response) =>
inventory.InfestedFoundry.AbilityOverrideUnlockCooldown &&
new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown
) {
handleSubsumeCompletion(inventory as unknown as IInventoryDatabaseDocument);
handleSubsumeCompletion(inventory);
await inventory.save();
}
//TODO: make a function that converts from database representation to client
const inventoryJSON = inventory.toJSON();
const inventoryResponse = toInventoryResponse(inventoryJSON);
const inventoryWithLoadOutPresets = await inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>(
"LoadOutPresets"
);
const inventoryWithLoadOutPresetsAndShips = await inventoryWithLoadOutPresets.populate<{ Ships: IShipInventory }>(
"Ships"
);
const inventoryResponse = inventoryWithLoadOutPresetsAndShips.toJSON<IInventoryResponse>();
if (config.infiniteCredits) {
inventoryResponse.RegularCredits = 999999999;

View File

@ -1,5 +1,5 @@
import { getAccountIdForRequest } from "@/src/services/loginService";
import { updateCurrencyByAccountId } from "@/src/services/inventoryService";
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
import { RequestHandler } from "express";
import { updateSlots } from "@/src/services/inventoryService";
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
@ -26,8 +26,10 @@ export const inventorySlotsController: RequestHandler = async (req, res) => {
//TODO: check which slot was purchased because pvpBonus is also possible
const currencyChanges = await updateCurrencyByAccountId(20, true, accountId);
await updateSlots(accountId, InventorySlot.PVE_LOADOUTS, 1, 1);
const inventory = await getInventory(accountId);
const currencyChanges = updateCurrency(inventory, 20, true);
updateSlots(inventory, InventorySlot.PVE_LOADOUTS, 1, 1);
await inventory.save();
//console.log({ InventoryChanges: currencyChanges }, " added loadout changes:");

View File

@ -34,9 +34,10 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
throw new Error(`unknown modular weapon type: ${data.WeaponType}`);
}
const category = modularWeaponTypes[data.WeaponType];
const inventory = await getInventory(accountId);
// Give weapon
const weapon = await addEquipment(category, data.WeaponType, accountId, data.Parts);
const weapon = addEquipment(inventory, category, data.WeaponType, data.Parts);
// Remove credits & parts
const miscItemChanges = [];
@ -46,7 +47,6 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
ItemCount: -1
});
}
const inventory = await getInventory(accountId);
const currencyChanges = updateCurrency(
inventory,
category == "Hoverboards" || category == "MoaPets" ? 5000 : 4000,

View File

@ -22,7 +22,6 @@ export const startRecipeController: RequestHandler = async (req, res) => {
const recipe = getRecipe(recipeName);
if (!recipe) {
logger.error(`unknown recipe ${recipeName}`);
throw new Error(`unknown recipe ${recipeName}`);
}

View File

@ -8,13 +8,16 @@ import {
} from "@/src/types/inventoryTypes/commonInventoryTypes";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getRecipeByResult } from "@/src/services/itemDataService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { addInfestedFoundryXP } from "./infestedFoundryController";
export const upgradesController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const payload = JSON.parse(String(req.body)) as IUpgradesRequest;
const inventory = await getInventory(accountId);
const inventoryChanges: IInventoryChanges = {};
for (const operation of payload.Operations) {
if (
operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" ||
@ -35,6 +38,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
const suit = inventory.Suits.find(x => x._id.toString() == payload.ItemId.$oid)!;
let newAbilityOverride: IAbilityOverride | undefined;
let totalPercentagePointsConsumed = 0;
if (operation.UpgradeRequirement != "") {
newAbilityOverride = {
Ability: operation.UpgradeRequirement,
@ -43,6 +47,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
const recipe = getRecipeByResult(operation.UpgradeRequirement)!;
for (const ingredient of recipe.ingredients) {
totalPercentagePointsConsumed += ingredient.ItemCount / 10;
inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -=
ingredient.ItemCount;
}
@ -52,6 +57,12 @@ export const upgradesController: RequestHandler = async (req, res) => {
suit.Configs[entry.Slot] ??= {};
suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride;
}
const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, totalPercentagePointsConsumed * 8);
addRecipes(inventory, recipeChanges);
inventoryChanges.Recipes = recipeChanges;
inventoryChanges.InfestedFoundry = inventory.toJSON().InfestedFoundry;
} else
switch (operation.UpgradeRequirement) {
case "/Lotus/Types/Items/MiscItems/OrokinReactor":
@ -146,7 +157,7 @@ export const upgradesController: RequestHandler = async (req, res) => {
}
}
await inventory.save();
res.json({ InventoryChanges: {} });
res.json({ InventoryChanges: inventoryChanges });
};
const setSlotPolarity = (item: IEquipmentDatabase, slot: number, polarity: ArtifactPolarity): void => {

View File

@ -1,7 +1,7 @@
import { getAccountIdForRequest } from "@/src/services/loginService";
import { ItemType, toAddItemRequest } from "@/src/helpers/customHelpers/addItemHelpers";
import { getWeaponType } from "@/src/services/itemDataService";
import { addPowerSuit, addEquipment } from "@/src/services/inventoryService";
import { addPowerSuit, addEquipment, getInventory } from "@/src/services/inventoryService";
import { RequestHandler } from "express";
const addItemController: RequestHandler = async (req, res) => {
@ -10,14 +10,18 @@ const addItemController: RequestHandler = async (req, res) => {
switch (request.type) {
case ItemType.Powersuit: {
const powersuit = await addPowerSuit(request.InternalName, accountId);
res.json(powersuit);
const inventory = await getInventory(accountId);
const inventoryChanges = addPowerSuit(inventory, request.InternalName);
await inventory.save();
res.json(inventoryChanges);
return;
}
case ItemType.Weapon: {
const inventory = await getInventory(accountId);
const weaponType = getWeaponType(request.InternalName);
const weapon = await addEquipment(weaponType, request.InternalName, accountId);
res.json(weapon);
const inventoryChanges = addEquipment(inventory, weaponType, request.InternalName);
await inventory.save();
res.json(inventoryChanges);
break;
}
default:

View File

@ -37,9 +37,20 @@ const getItemListsController: RequestHandler = (req, res) => {
}
}
for (const [uniqueName, item] of Object.entries(ExportResources)) {
let name = getString(item.name, lang);
if ("dissectionParts" in item) {
name = getString("/Lotus/Language/Fish/FishDisplayName", lang).split("|FISH_NAME|").join(name);
if (uniqueName.indexOf("Large") != -1) {
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeLargeAbbrev", lang));
} else if (uniqueName.indexOf("Medium") != -1) {
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeMediumAbbrev", lang));
} else {
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeSmallAbbrev", lang));
}
}
miscitems.push({
uniqueName: item.productCategory + ":" + uniqueName,
name: getString(item.name, lang)
name: name
});
}
for (const [uniqueName, item] of Object.entries(ExportGear)) {

View File

@ -0,0 +1,5 @@
import { RequestHandler } from "express";
export const getSkuCatalogController: RequestHandler = (_req, res) => {
res.sendFile("static/fixed_responses/getSkuCatalog.json", { root: "./" });
};

View File

@ -1,14 +1,6 @@
import { IMongoDate, IOid } from "@/src/types/commonTypes";
import { IInventoryResponse } from "@/src/types/inventoryTypes/inventoryTypes";
import { Types } from "mongoose";
//TODO: this needs to be addressed: a schema's toJSON is responsible for changing Oid and Date to their corresponding Response versions __id to "ItemId":{"$oid":"6450f720bc562ebf030222d4"}, and a Date to "date":{"$date":{"$numberLong":"unix timestamp"})
export const toInventoryResponse = (inventoryDatabase: { accountOwnerId: Types.ObjectId }): IInventoryResponse => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { accountOwnerId, ...inventoryResponse } = inventoryDatabase;
return inventoryResponse as unknown as IInventoryResponse;
};
export const toOid = (objectId: Types.ObjectId): IOid => {
return { $oid: objectId.toString() } satisfies IOid;
};

View File

@ -0,0 +1,16 @@
import { NextFunction, Request, Response } from "express";
import { logger } from "../utils/logger";
export const errorHandler = (err: Error, req: Request, res: Response, _next: NextFunction): void => {
if (err.message == "Invalid accountId-nonce pair") {
res.status(400).send("Log-in expired");
} else if (err.stack) {
const stackArr = err.stack.split("\n");
stackArr[0] += ` while processing ${req.path} request`;
logger.error(stackArr.join("\n"));
res.status(500).end();
} else {
logger.error(`uncaught error while processing ${req.path} request: ${err.message}`);
res.status(500).end();
}
};

View File

@ -1,4 +1,4 @@
import { Model, Schema, Types, model } from "mongoose";
import { Document, Model, Schema, Types, model } from "mongoose";
import {
IFlavourItem,
IRawUpgrade,
@ -45,7 +45,8 @@ import {
ICrewShipMembers,
ICrewShip,
ICrewShipPilotWeapon,
IShipExterior
IShipExterior,
IHelminthFoodRecord
} from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes";
import {
@ -233,6 +234,9 @@ const EquipmentSchema = new Schema<IEquipmentDatabase>(
UnlockLevel: Number,
Expiry: Date,
SkillTree: String,
OffensiveUpgrade: String,
DefensiveUpgrade: String,
UpgradesExpiry: Date,
ArchonCrystalUpgrades: { type: [ArchonCrystalUpgradeSchema], default: undefined }
},
{ id: false }
@ -467,7 +471,22 @@ const consumedSchuitsSchema = new Schema<IConsumedSuit>(
{ _id: false }
);
const helminthResourceSchema = new Schema<IHelminthResource>({ ItemType: String, Count: Number }, { _id: false });
const helminthFoodRecordSchema = new Schema<IHelminthFoodRecord>(
{
ItemType: String,
Date: Number
},
{ _id: false }
);
const helminthResourceSchema = new Schema<IHelminthResource>(
{
ItemType: String,
Count: Number,
RecentlyConvertedResources: { type: [helminthFoodRecordSchema], default: undefined }
},
{ _id: false }
);
const infestedFoundrySchema = new Schema<IInfestedFoundry>(
{
@ -1061,6 +1080,7 @@ inventorySchema.set("toJSON", {
transform(_document, returnedObject) {
delete returnedObject._id;
delete returnedObject.__v;
delete returnedObject.accountOwnerId;
const inventoryDatabase = returnedObject as IInventoryDatabase;
const inventoryResponse = returnedObject as IInventoryResponse;
@ -1114,3 +1134,15 @@ type InventoryDocumentProps = {
type InventoryModelType = Model<IInventoryDatabase, {}, InventoryDocumentProps>;
export const Inventory = model<IInventoryDatabase, InventoryModelType>("Inventory", inventorySchema);
// eslint-disable-next-line @typescript-eslint/ban-types
export type TInventoryDatabaseDocument = Document<unknown, {}, IInventoryDatabase> &
Omit<
IInventoryDatabase & {
_id: Types.ObjectId;
} & {
__v: number;
},
keyof InventoryDocumentProps
> &
InventoryDocumentProps;

View File

@ -5,6 +5,7 @@ import { artifactsController } from "../controllers/api/artifactsController";
import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
import { createGuildController } from "@/src/controllers/api/createGuildController";
import { creditsController } from "@/src/controllers/api/creditsController";
import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
import { dojoController } from "@/src/controllers/api/dojoController";
import { dronesController } from "@/src/controllers/api/dronesController";
@ -16,7 +17,6 @@ import { focusController } from "@/src/controllers/api/focusController";
import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
import { getAllianceController } from "@/src/controllers/api/getAllianceController";
import { getCreditsController } from "@/src/controllers/api/getCreditsController";
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
import { getFriendsController } from "@/src/controllers/api/getFriendsController";
import { getGuildController } from "@/src/controllers/api/getGuildController";
@ -76,7 +76,7 @@ const apiRouter = express.Router();
// get
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
apiRouter.get("/credits.php", getCreditsController);
apiRouter.get("/credits.php", creditsController);
apiRouter.get("/deleteSession.php", deleteSessionController);
apiRouter.get("/dojo", dojoController);
apiRouter.get("/drones.php", dronesController);

11
src/routes/pay.ts Normal file
View File

@ -0,0 +1,11 @@
import express from "express";
import { getSkuCatalogController } from "@/src/controllers/pay/getSkuCatalogController";
import { steamPacksController } from "@/src/controllers/pay/steamPacksController";
const payRouter = express.Router();
payRouter.get("/getSkuCatalog.php", getSkuCatalogController);
payRouter.post("/steamPacks.php", steamPacksController);
export { payRouter };

View File

@ -2,7 +2,7 @@ import { Request } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import { Guild } from "@/src/models/guildModel";
import { IInventoryDatabaseDocument } from "../types/inventoryTypes/inventoryTypes";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
export const getGuildForRequest = async (req: Request) => {
const accountId = await getAccountIdForRequest(req);
@ -10,7 +10,7 @@ export const getGuildForRequest = async (req: Request) => {
return await getGuildForRequestEx(req, inventory);
};
export const getGuildForRequestEx = async (req: Request, inventory: IInventoryDatabaseDocument) => {
export const getGuildForRequestEx = async (req: Request, inventory: TInventoryDatabaseDocument) => {
const guildId = req.query.guildId as string;
if (!inventory.GuildId || inventory.GuildId.toString() != guildId) {
throw new Error("Account is not in the guild that it has sent a request for");

View File

@ -1,4 +1,4 @@
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import postTutorialInventory from "@/static/fixed_responses/postTutorialInventory.json";
import { config } from "@/src/services/configService";
import { Types } from "mongoose";
@ -7,7 +7,6 @@ import {
IChallengeProgress,
IConsumable,
IFlavourItem,
IInventoryDatabaseDocument,
IMiscItem,
IMission,
IRawUpgrade,
@ -80,8 +79,9 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
}
} else if (typeof delta[key] == "object") {
console.assert(key.substring(-3) == "Bin");
console.assert(key != "InfestedFoundry");
const left = InventoryChanges[key] as IBinChanges;
const right: IBinChanges = delta[key];
const right = delta[key] as IBinChanges;
left.count += right.count;
left.platinum += right.platinum;
left.Slots += right.Slots;
@ -95,7 +95,7 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
}
};
export const getInventory = async (accountOwnerId: string) => {
export const getInventory = async (accountOwnerId: string): Promise<TInventoryDatabaseDocument> => {
const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId });
if (!inventory) {
@ -106,13 +106,12 @@ export const getInventory = async (accountOwnerId: string) => {
};
export const addItem = async (
accountId: string,
inventory: TInventoryDatabaseDocument,
typeName: string,
quantity: number = 1
): Promise<{ InventoryChanges: IInventoryChanges }> => {
// Strict typing
if (typeName in ExportRecipes) {
const inventory = await getInventory(accountId);
const recipeChanges = [
{
ItemType: typeName,
@ -120,7 +119,6 @@ export const addItem = async (
} satisfies ITypeCount
];
addRecipes(inventory, recipeChanges);
await inventory.save();
return {
InventoryChanges: {
Recipes: recipeChanges
@ -128,11 +126,9 @@ export const addItem = async (
};
}
if (typeName in ExportResources) {
const inventory = await getInventory(accountId);
if (ExportResources[typeName].productCategory == "Ships") {
const oid = await createShip(new Types.ObjectId(accountId), typeName);
const oid = await createShip(inventory.accountOwnerId, typeName);
inventory.Ships.push(oid);
await inventory.save();
return {
InventoryChanges: {
Ships: [
@ -144,11 +140,8 @@ export const addItem = async (
}
};
} else if (ExportResources[typeName].productCategory == "CrewShips") {
return {
InventoryChanges: {
CrewShips: [await addCrewShip(typeName, accountId)]
}
};
const inventoryChanges = addCrewShip(inventory, typeName);
return { InventoryChanges: inventoryChanges };
} else {
const miscItemChanges = [
{
@ -157,7 +150,6 @@ export const addItem = async (
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
@ -166,21 +158,14 @@ export const addItem = async (
}
}
if (typeName in ExportCustoms) {
return {
InventoryChanges: {
WeaponSkins: [await addSkin(typeName, accountId)]
}
};
const inventoryChanges = addSkin(inventory, typeName);
return { InventoryChanges: inventoryChanges };
}
if (typeName in ExportFlavour) {
return {
InventoryChanges: {
FlavourItems: [await addCustomization(typeName, accountId)]
}
};
const inventoryChanges = addCustomization(inventory, typeName);
return { InventoryChanges: inventoryChanges };
}
if (typeName in ExportUpgrades || typeName in ExportArcanes) {
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
@ -188,7 +173,6 @@ export const addItem = async (
}
];
addMods(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
RawUpgrades: changes
@ -196,7 +180,6 @@ export const addItem = async (
};
}
if (typeName in ExportGear) {
const inventory = await getInventory(accountId);
const consumablesChanges = [
{
ItemType: typeName,
@ -204,7 +187,6 @@ export const addItem = async (
} satisfies IConsumable
];
addConsumables(inventory, consumablesChanges);
await inventory.save();
return {
InventoryChanges: {
Consumables: consumablesChanges
@ -217,8 +199,8 @@ export const addItem = async (
case "Powersuits":
switch (typeName.substr(1).split("/")[2]) {
default: {
const inventoryChanges = await addPowerSuit(typeName, accountId);
await updateSlots(accountId, InventorySlot.SUITS, 0, 1);
const inventoryChanges = addPowerSuit(inventory, typeName);
updateSlots(inventory, InventorySlot.SUITS, 0, 1);
return {
InventoryChanges: {
...inventoryChanges,
@ -231,22 +213,22 @@ export const addItem = async (
};
}
case "Archwing": {
const spaceSuit = await addSpaceSuit(typeName, accountId);
await updateSlots(accountId, InventorySlot.SPACESUITS, 0, 1);
const inventoryChanges = addSpaceSuit(inventory, typeName);
updateSlots(inventory, InventorySlot.SPACESUITS, 0, 1);
return {
InventoryChanges: {
...inventoryChanges,
SpaceSuitBin: {
count: 1,
platinum: 0,
Slots: -1
},
SpaceSuits: [spaceSuit]
}
}
};
}
case "EntratiMech": {
const inventoryChanges = await addMechSuit(typeName, accountId);
await updateSlots(accountId, InventorySlot.MECHSUITS, 0, 1);
const inventoryChanges = addMechSuit(inventory, typeName);
updateSlots(inventory, InventorySlot.MECHSUITS, 0, 1);
return {
InventoryChanges: {
...inventoryChanges,
@ -262,18 +244,17 @@ export const addItem = async (
break;
case "Weapons": {
const weaponType = getWeaponType(typeName);
const weapon = await addEquipment(weaponType, typeName, accountId);
await updateSlots(accountId, InventorySlot.WEAPONS, 0, 1);
const inventoryChanges = addEquipment(inventory, weaponType, typeName);
updateSlots(inventory, InventorySlot.WEAPONS, 0, 1);
return {
InventoryChanges: {
WeaponBin: { count: 1, platinum: 0, Slots: -1 },
[weaponType]: [weapon]
...inventoryChanges,
WeaponBin: { count: 1, platinum: 0, Slots: -1 }
}
};
}
case "Objects": {
// /Lotus/Objects/Tenno/Props/TnoLisetTextProjector (Note Beacon)
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
@ -281,7 +262,6 @@ export const addItem = async (
} satisfies IMiscItem
];
addShipDecorations(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
ShipDecorations: changes
@ -291,8 +271,8 @@ export const addItem = async (
case "Types":
switch (typeName.substr(1).split("/")[2]) {
case "Sentinels": {
const inventoryChanges = await addSentinel(typeName, accountId);
await updateSlots(accountId, InventorySlot.SENTINELS, 0, 1);
const inventoryChanges = addSentinel(inventory, typeName);
updateSlots(inventory, InventorySlot.SENTINELS, 0, 1);
return {
InventoryChanges: {
...inventoryChanges,
@ -303,7 +283,6 @@ export const addItem = async (
case "Items": {
switch (typeName.substr(1).split("/")[3]) {
case "ShipDecos": {
const inventory = await getInventory(accountId);
const changes = [
{
ItemType: typeName,
@ -311,7 +290,6 @@ export const addItem = async (
} satisfies IMiscItem
];
addShipDecorations(inventory, changes);
await inventory.save();
return {
InventoryChanges: {
ShipDecorations: changes
@ -319,7 +297,6 @@ export const addItem = async (
};
}
default: {
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
@ -327,7 +304,6 @@ export const addItem = async (
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
@ -339,7 +315,6 @@ export const addItem = async (
case "Game":
if (typeName.substr(1).split("/")[3] == "Projections") {
// Void Relics, e.g. /Lotus/Types/Game/Projections/T2VoidProjectionGaussPrimeDBronze
const inventory = await getInventory(accountId);
const miscItemChanges = [
{
ItemType: typeName,
@ -347,7 +322,6 @@ export const addItem = async (
} satisfies IMiscItem
];
addMiscItems(inventory, miscItemChanges);
await inventory.save();
return {
InventoryChanges: {
MiscItems: miscItemChanges
@ -364,13 +338,13 @@ export const addItem = async (
};
//TODO: maybe genericMethod for all the add methods, they share a lot of logic
export const addSentinel = async (sentinelName: string, accountId: string): Promise<IInventoryChanges> => {
const inventoryChanges: IInventoryChanges = {};
export const addSentinel = (
inventory: TInventoryDatabaseDocument,
sentinelName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
if (ExportSentinels[sentinelName]?.defaultWeapon) {
inventoryChanges.SentinelWeapons = [
await addSentinelWeapon(ExportSentinels[sentinelName].defaultWeapon, accountId)
];
addSentinelWeapon(inventory, ExportSentinels[sentinelName].defaultWeapon, inventoryChanges);
}
const modsToGive: IRawUpgrade[] = [];
@ -388,96 +362,109 @@ export const addSentinel = async (sentinelName: string, accountId: string): Prom
}
}
const inventory = await getInventory(accountId);
addMods(inventory, modsToGive);
const sentinelIndex = inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0 });
const changedInventory = await inventory.save();
inventoryChanges.Sentinels = [changedInventory.Sentinels[sentinelIndex - 1].toJSON()];
const sentinelIndex = inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0 }) - 1;
inventoryChanges.Sentinels ??= [];
(inventoryChanges.Sentinels as IEquipmentClient[]).push(
inventory.Sentinels[sentinelIndex].toJSON<IEquipmentClient>()
);
return inventoryChanges;
};
export const addSentinelWeapon = async (typeName: string, accountId: string): Promise<IEquipmentClient> => {
const inventory = await getInventory(accountId);
const sentinelIndex = inventory.SentinelWeapons.push({ ItemType: typeName });
const changedInventory = await inventory.save();
return changedInventory.SentinelWeapons[sentinelIndex - 1].toJSON<IEquipmentClient>();
export const addSentinelWeapon = (
inventory: TInventoryDatabaseDocument,
typeName: string,
inventoryChanges: IInventoryChanges
): void => {
const index = inventory.SentinelWeapons.push({ ItemType: typeName }) - 1;
inventoryChanges.SentinelWeapons ??= [];
(inventoryChanges.SentinelWeapons as IEquipmentClient[]).push(
inventory.SentinelWeapons[index].toJSON<IEquipmentClient>()
);
};
export const addPowerSuit = async (powersuitName: string, accountId: string): Promise<IInventoryChanges> => {
const inventoryChanges: IInventoryChanges = {};
export const addPowerSuit = (
inventory: TInventoryDatabaseDocument,
powersuitName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const specialItems = getExalted(powersuitName);
if (specialItems) {
for await (const specialItem of specialItems) {
await addSpecialItem(specialItem, accountId, inventoryChanges);
for (const specialItem of specialItems) {
addSpecialItem(inventory, specialItem, inventoryChanges);
}
}
const inventory = await getInventory(accountId);
const suitIndex = inventory.Suits.push({ ItemType: powersuitName, Configs: [], UpgradeVer: 101, XP: 0 });
const changedInventory = await inventory.save();
inventoryChanges.Suits = [changedInventory.Suits[suitIndex - 1].toJSON()];
const suitIndex = inventory.Suits.push({ ItemType: powersuitName, Configs: [], UpgradeVer: 101, XP: 0 }) - 1;
inventoryChanges.Suits ??= [];
(inventoryChanges.Suits as IEquipmentClient[]).push(inventory.Suits[suitIndex].toJSON<IEquipmentClient>());
return inventoryChanges;
};
export const addMechSuit = async (mechsuitName: string, accountId: string): Promise<IInventoryChanges> => {
const inventoryChanges: IInventoryChanges = {};
export const addMechSuit = (
inventory: TInventoryDatabaseDocument,
mechsuitName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const specialItems = getExalted(mechsuitName);
if (specialItems) {
for await (const specialItem of specialItems) {
await addSpecialItem(specialItem, accountId, inventoryChanges);
for (const specialItem of specialItems) {
addSpecialItem(inventory, specialItem, inventoryChanges);
}
}
const inventory = await getInventory(accountId);
const suitIndex = inventory.MechSuits.push({ ItemType: mechsuitName, Configs: [], UpgradeVer: 101, XP: 0 });
const changedInventory = await inventory.save();
inventoryChanges.MechSuits = [changedInventory.MechSuits[suitIndex - 1].toJSON()];
const suitIndex = inventory.MechSuits.push({ ItemType: mechsuitName, Configs: [], UpgradeVer: 101, XP: 0 }) - 1;
inventoryChanges.MechSuits ??= [];
(inventoryChanges.MechSuits as IEquipmentClient[]).push(inventory.MechSuits[suitIndex].toJSON<IEquipmentClient>());
return inventoryChanges;
};
export const addSpecialItem = async (
export const addSpecialItem = (
inventory: TInventoryDatabaseDocument,
itemName: string,
accountId: string,
inventoryChanges: IInventoryChanges
): Promise<void> => {
const inventory = await getInventory(accountId);
): void => {
if (inventory.SpecialItems.find(x => x.ItemType == itemName)) {
return;
}
const specialItemIndex = inventory.SpecialItems.push({
ItemType: itemName,
Configs: [],
Features: 1,
UpgradeVer: 101,
XP: 0
});
const changedInventory = await inventory.save();
const specialItemIndex =
inventory.SpecialItems.push({
ItemType: itemName,
Configs: [],
Features: 1,
UpgradeVer: 101,
XP: 0
}) - 1;
inventoryChanges.SpecialItems ??= [];
(inventoryChanges.SpecialItems as object[]).push(changedInventory.SpecialItems[specialItemIndex - 1].toJSON());
(inventoryChanges.SpecialItems as IEquipmentClient[]).push(
inventory.SpecialItems[specialItemIndex].toJSON<IEquipmentClient>()
);
};
export const addSpaceSuit = async (spacesuitName: string, accountId: string): Promise<IEquipmentClient> => {
const inventory = await getInventory(accountId);
const suitIndex = inventory.SpaceSuits.push({ ItemType: spacesuitName, Configs: [], UpgradeVer: 101, XP: 0 });
const changedInventory = await inventory.save();
return changedInventory.SpaceSuits[suitIndex - 1].toJSON<IEquipmentClient>();
export const addSpaceSuit = (
inventory: TInventoryDatabaseDocument,
spacesuitName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const suitIndex = inventory.SpaceSuits.push({ ItemType: spacesuitName, Configs: [], UpgradeVer: 101, XP: 0 }) - 1;
inventoryChanges.SpaceSuits ??= [];
(inventoryChanges.SpaceSuits as IEquipmentClient[]).push(
inventory.SpaceSuits[suitIndex].toJSON<IEquipmentClient>()
);
return inventoryChanges;
};
export const updateSlots = async (
accountId: string,
export const updateSlots = (
inventory: TInventoryDatabaseDocument,
slotName: SlotNames,
slotAmount: number,
extraAmount: number
): Promise<void> => {
const inventory = await getInventory(accountId);
): void => {
inventory[slotName].Slots += slotAmount;
if (inventory[slotName].Extra === undefined) {
inventory[slotName].Extra = extraAmount;
} else {
inventory[slotName].Extra += extraAmount;
}
await inventory.save();
};
const isCurrencyTracked = (usePremium: boolean): boolean => {
@ -485,7 +472,7 @@ const isCurrencyTracked = (usePremium: boolean): boolean => {
};
export const updateCurrency = (
inventory: IInventoryDatabaseDocument,
inventory: TInventoryDatabaseDocument,
price: number,
usePremium: boolean
): ICurrencyChanges => {
@ -549,48 +536,65 @@ export const updateTheme = async (data: IThemeUpdateRequest, accountId: string):
await inventory.save();
};
export const addEquipment = async (
export const addEquipment = (
inventory: TInventoryDatabaseDocument,
category: TEquipmentKey,
type: string,
accountId: string,
modularParts: string[] | undefined = undefined
): Promise<IEquipmentClient> => {
const inventory = await getInventory(accountId);
modularParts: string[] | undefined = undefined,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const index =
inventory[category].push({
ItemType: type,
Configs: [],
XP: 0,
ModularParts: modularParts
}) - 1;
const index = inventory[category].push({
ItemType: type,
Configs: [],
XP: 0,
ModularParts: modularParts
});
const changedInventory = await inventory.save();
return changedInventory[category][index - 1].toJSON() as object as IEquipmentClient;
inventoryChanges[category] ??= [];
(inventoryChanges[category] as IEquipmentClient[]).push(inventory[category][index].toJSON<IEquipmentClient>());
return inventoryChanges;
};
export const addCustomization = async (customizatonName: string, accountId: string): Promise<IFlavourItem> => {
const inventory = await getInventory(accountId);
const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizatonName }) - 1;
const changedInventory = await inventory.save();
return changedInventory.FlavourItems[flavourItemIndex].toJSON();
export const addCustomization = (
inventory: TInventoryDatabaseDocument,
customizationName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1;
inventoryChanges.FlavourItems ??= [];
(inventoryChanges.FlavourItems as IFlavourItem[]).push(
inventory.FlavourItems[flavourItemIndex].toJSON<IFlavourItem>()
);
return inventoryChanges;
};
export const addSkin = async (typeName: string, accountId: string): Promise<IWeaponSkinClient> => {
const inventory = await getInventory(accountId);
export const addSkin = (
inventory: TInventoryDatabaseDocument,
typeName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const index = inventory.WeaponSkins.push({ ItemType: typeName }) - 1;
const changedInventory = await inventory.save();
return changedInventory.WeaponSkins[index].toJSON() as object as IWeaponSkinClient;
inventoryChanges.WeaponSkins ??= [];
(inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push(
inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>()
);
return inventoryChanges;
};
const addCrewShip = async (typeName: string, accountId: string) => {
const inventory = await getInventory(accountId);
const addCrewShip = (
inventory: TInventoryDatabaseDocument,
typeName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const index = inventory.CrewShips.push({ ItemType: typeName }) - 1;
const changedInventory = await inventory.save();
return changedInventory.CrewShips[index].toJSON();
inventoryChanges.CrewShips ??= [];
(inventoryChanges.CrewShips as object[]).push(inventory.CrewShips[index].toJSON());
return inventoryChanges;
};
const addGearExpByCategory = (
inventory: IInventoryDatabaseDocument,
inventory: TInventoryDatabaseDocument,
gearArray: IEquipmentClient[] | undefined,
categoryName: TEquipmentKey
): void => {
@ -622,7 +626,7 @@ const addGearExpByCategory = (
});
};
export const addMiscItems = (inventory: IInventoryDatabaseDocument, itemsArray: IMiscItem[] | undefined): void => {
export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray: IMiscItem[] | undefined): void => {
const { MiscItems } = inventory;
itemsArray?.forEach(({ ItemCount, ItemType }) => {
@ -638,7 +642,7 @@ export const addMiscItems = (inventory: IInventoryDatabaseDocument, itemsArray:
};
export const addShipDecorations = (
inventory: IInventoryDatabaseDocument,
inventory: TInventoryDatabaseDocument,
itemsArray: IConsumable[] | undefined
): void => {
const { ShipDecorations } = inventory;
@ -655,7 +659,7 @@ export const addShipDecorations = (
});
};
export const addConsumables = (inventory: IInventoryDatabaseDocument, itemsArray: IConsumable[] | undefined): void => {
export const addConsumables = (inventory: TInventoryDatabaseDocument, itemsArray: IConsumable[] | undefined): void => {
const { Consumables } = inventory;
itemsArray?.forEach(({ ItemCount, ItemType }) => {
@ -670,7 +674,7 @@ export const addConsumables = (inventory: IInventoryDatabaseDocument, itemsArray
});
};
export const addRecipes = (inventory: IInventoryDatabaseDocument, itemsArray: ITypeCount[] | undefined): void => {
export const addRecipes = (inventory: TInventoryDatabaseDocument, itemsArray: ITypeCount[] | undefined): void => {
const { Recipes } = inventory;
itemsArray?.forEach(({ ItemCount, ItemType }) => {
@ -685,7 +689,7 @@ export const addRecipes = (inventory: IInventoryDatabaseDocument, itemsArray: IT
});
};
export const addMods = (inventory: IInventoryDatabaseDocument, itemsArray: IRawUpgrade[] | undefined): void => {
export const addMods = (inventory: TInventoryDatabaseDocument, itemsArray: IRawUpgrade[] | undefined): void => {
const { RawUpgrades } = inventory;
itemsArray?.forEach(({ ItemType, ItemCount }) => {
const itemIndex = RawUpgrades.findIndex(i => i.ItemType === ItemType);
@ -700,7 +704,7 @@ export const addMods = (inventory: IInventoryDatabaseDocument, itemsArray: IRawU
};
export const addFusionTreasures = (
inventory: IInventoryDatabaseDocument,
inventory: TInventoryDatabaseDocument,
itemsArray: IFusionTreasure[] | undefined
): void => {
const { FusionTreasures } = inventory;
@ -729,7 +733,7 @@ export const updateChallengeProgress = async (
};
export const addSeasonalChallengeHistory = (
inventory: IInventoryDatabaseDocument,
inventory: TInventoryDatabaseDocument,
itemsArray: ISeasonChallenge[] | undefined
): void => {
const category = inventory.SeasonChallengeHistory;
@ -746,7 +750,7 @@ export const addSeasonalChallengeHistory = (
};
export const addChallenges = (
inventory: IInventoryDatabaseDocument,
inventory: TInventoryDatabaseDocument,
itemsArray: IChallengeProgress[] | undefined
): void => {
const category = inventory.ChallengeProgress;
@ -763,7 +767,7 @@ export const addChallenges = (
});
};
const addMissionComplete = (inventory: IInventoryDatabaseDocument, { Tag, Completes }: IMission): void => {
const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes }: IMission): void => {
const { Missions } = inventory;
const itemIndex = Missions.findIndex(item => item.Tag === Tag);
@ -865,7 +869,7 @@ export const addBooster = async (ItemType: string, time: number, accountId: stri
existingBooster.ExpiryDate = Math.max(existingBooster.ExpiryDate, currentTime) + time;
inventory.markModified(`Boosters.${itemIndex}.ExpiryDate`);
} else {
Boosters.push({ ItemType, ExpiryDate: currentTime + time }) - 1;
Boosters.push({ ItemType, ExpiryDate: currentTime + time });
}
await inventory.save();

View File

@ -1,5 +1,4 @@
import { getIndexAfter } from "@/src/helpers/stringHelpers";
import { logger } from "@/src/utils/logger";
import {
dict_de,
dict_en,
@ -54,7 +53,6 @@ export const getWeaponType = (weaponName: string): WeaponTypeInternal => {
const weaponType = weaponInfo.productCategory;
if (!weaponType) {
logger.error(`unknown weapon category for item ${weaponName}`);
throw new Error(`unknown weapon category for item ${weaponName}`);
}
@ -83,7 +81,6 @@ export const getItemCategoryByUniqueName = (uniqueName: string): string => {
const index = getIndexAfter(uniqueName, splitWord);
if (index === -1) {
logger.error(`error parsing item category ${uniqueName}`);
throw new Error(`error parsing item category ${uniqueName}`);
}
const category = uniqueName.substring(index).split("/")[0];

View File

@ -1,12 +1,10 @@
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
import { logger } from "@/src/utils/logger";
export const getLoadout = async (accountId: string) => {
const loadout = await Loadout.findOne({ loadoutOwnerId: accountId });
if (!loadout) {
logger.error(`loadout not found for account ${accountId}`);
throw new Error("loadout not found");
throw new Error(`loadout not found for account ${accountId}`);
}
return loadout;

View File

@ -1,12 +1,10 @@
import { PersonalRooms } from "@/src/models/personalRoomsModel";
import { logger } from "@/src/utils/logger";
export const getPersonalRooms = async (accountId: string) => {
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
if (!personalRooms) {
logger.error(`personal rooms not found for account ${accountId}`);
throw new Error("personal rooms not found");
throw new Error(`personal rooms not found for account ${accountId}`);
}
return personalRooms;
};

View File

@ -204,9 +204,12 @@ export const handleStoreItemAcquisition = async (
}
}
switch (storeCategory) {
default:
purchaseResponse = await addItem(accountId, internalName, quantity);
default: {
const inventory = await getInventory(accountId);
purchaseResponse = await addItem(inventory, internalName, quantity);
await inventory.save();
break;
}
case "Types":
purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity);
break;
@ -237,7 +240,8 @@ export const slotPurchaseNameToSlotName: SlotPurchase = {
// // number of frames = extra - slots + 2
const handleSlotPurchase = async (
slotPurchaseNameFull: string,
accountId: string
accountId: string,
quantity: number
): Promise<{ InventoryChanges: IInventoryChanges }> => {
logger.debug(`slot name ${slotPurchaseNameFull}`);
const slotPurchaseName = parseSlotPurchaseName(
@ -246,19 +250,21 @@ const handleSlotPurchase = async (
logger.debug(`slot purchase name ${slotPurchaseName}`);
const slotName = slotPurchaseNameToSlotName[slotPurchaseName].name;
const slotsPerPurchase = slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase;
const slotsPurchased = slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase * quantity;
await updateSlots(accountId, slotName, slotsPerPurchase, slotsPerPurchase);
const inventory = await getInventory(accountId);
updateSlots(inventory, slotName, slotsPurchased, slotsPurchased);
await inventory.save();
logger.debug(`added ${slotsPerPurchase} slot ${slotName}`);
logger.debug(`added ${slotsPurchased} slot ${slotName}`);
return {
InventoryChanges: {
[slotName]: {
count: 0,
platinum: 1,
Slots: slotsPerPurchase,
Extra: slotsPerPurchase
Slots: slotsPurchased,
Extra: slotsPurchased
}
}
};
@ -277,6 +283,7 @@ const handleBoosterPackPurchase = async (
BoosterPackItems: "",
InventoryChanges: {}
};
const inventory = await getInventory(accountId);
for (let i = 0; i != quantity; ++i) {
for (const weights of pack.rarityWeightsPerRoll) {
const result = getRandomWeightedReward(pack.components, weights);
@ -286,11 +293,12 @@ const handleBoosterPackPurchase = async (
result.type.split("/Lotus/").join("/Lotus/StoreItems/") + ',{"lvl":0};';
combineInventoryChanges(
purchaseResponse.InventoryChanges,
(await addItem(accountId, result.type, result.itemCount)).InventoryChanges
(await addItem(inventory, result.type, result.itemCount)).InventoryChanges
);
}
}
}
await inventory.save();
return purchaseResponse;
};
@ -303,12 +311,16 @@ const handleTypesPurchase = async (
const typeCategory = getStoreItemTypesCategory(typesName);
logger.debug(`type category ${typeCategory}`);
switch (typeCategory) {
default:
return await addItem(accountId, typesName, quantity);
default: {
const inventory = await getInventory(accountId);
const resp = await addItem(inventory, typesName, quantity);
await inventory.save();
return resp;
}
case "BoosterPacks":
return await handleBoosterPackPurchase(typesName, accountId, quantity);
case "SlotItems":
return await handleSlotPurchase(typesName, accountId);
return await handleSlotPurchase(typesName, accountId, quantity);
}
};

View File

@ -47,7 +47,6 @@ export const handleSetShipDecorations = async (
const roomToPlaceIn = rooms.find(room => room.Name === placedDecoration.Room);
if (!roomToPlaceIn) {
logger.error("room not found");
throw new Error("room not found");
}
@ -59,7 +58,6 @@ export const handleSetShipDecorations = async (
);
if (existingDecorationIndex === -1) {
logger.error("decoration to be moved not found");
throw new Error("decoration to be moved not found");
}
@ -143,13 +141,11 @@ export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlaced
const room = personalRooms.Ship.Rooms.find(room => room.Name === req.Room);
if (!room) {
logger.error("room not found");
throw new Error("room not found");
}
const placedDeco = room.PlacedDecos?.find(x => x._id.toString() == req.DecoId);
if (!placedDeco) {
logger.error("deco not found");
throw new Error("deco not found");
}

View File

@ -1,6 +1,5 @@
import { Ship } from "@/src/models/shipModel";
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
import { logger } from "@/src/utils/logger";
import { Types } from "mongoose";
export const createShip = async (
@ -26,7 +25,6 @@ export const getShip = async (shipId: Types.ObjectId, fieldSelection: string = "
const ship = await Ship.findOne({ _id: shipId }, fieldSelection);
if (!ship) {
logger.error(`error finding a ship with id ${shipId.toString()}`);
throw new Error(`error finding a ship with id ${shipId.toString()}`);
}
@ -39,7 +37,6 @@ export const getShipLean = async (shipOwnerId: string) => {
}>("LoadOutInventory.LoadOutPresets");
if (!ship) {
logger.error(`error finding a ship for account ${shipOwnerId}`);
throw new Error(`error finding a ship for account ${shipOwnerId}`);
}

View File

@ -78,8 +78,9 @@ export interface IEquipmentSelection {
hide?: boolean;
}
export interface IEquipmentClient extends Omit<IEquipmentDatabase, "_id"> {
export interface IEquipmentClient extends Omit<IEquipmentDatabase, "_id" | "UpgradesExpiry"> {
ItemId: IOid;
UpgradesExpiry?: IMongoDate;
}
export enum EquipmentFeatures {
@ -112,6 +113,9 @@ export interface IEquipmentDatabase {
UnlockLevel?: number;
Expiry?: IMongoDate;
SkillTree?: string;
OffensiveUpgrade?: string;
DefensiveUpgrade?: string;
UpgradesExpiry?: Date;
ArchonCrystalUpgrades?: IArchonCrystalUpgrade[];
_id: Types.ObjectId;
}

View File

@ -10,8 +10,6 @@ import {
IEquipmentDatabase
} from "@/src/types/inventoryTypes/commonInventoryTypes";
//Document extends will be deleted soon. TODO: delete and migrate uses to ...
export interface IInventoryDatabaseDocument extends IInventoryDatabase, Document {}
export interface IInventoryDatabase
extends Omit<
IInventoryResponse,
@ -515,13 +513,15 @@ export interface IFusionTreasure {
Sockets: number;
}
export interface IHelminthFoodRecord {
ItemType: string;
Date: number;
}
export interface IHelminthResource {
ItemType: string;
Count: number;
RecentlyConvertedResources?: {
ItemType: string;
Date: number;
}[];
RecentlyConvertedResources?: IHelminthFoodRecord[];
}
export interface IInfestedFoundry {

View File

@ -1,3 +1,5 @@
import { IInfestedFoundry } from "./inventoryTypes/inventoryTypes";
export interface IPurchaseRequest {
PurchaseParams: IPurchaseParams;
buildLabel: string;
@ -25,8 +27,10 @@ export interface ICurrencyChanges {
export type IInventoryChanges = {
[_ in SlotNames]?: IBinChanges;
} & ICurrencyChanges &
Record<string, IBinChanges | number | object[]>;
} & ICurrencyChanges & { InfestedFoundry?: IInfestedFoundry } & Record<
string,
IBinChanges | number | object[] | IInfestedFoundry
>;
export interface IPurchaseResponse {
InventoryChanges: IInventoryChanges;

View File

@ -4346,5 +4346,17 @@
{
"scans": 9999,
"type": "/Lotus/Weapons/Infested/Melee/InfBoomerang/InfBoomerangSpawnAvatar"
},
{
"scans": 9999,
"type": "/Lotus/Types/Game/CrewShip/GrineerDestroyer/DeepSpace/GrineerDSDestroyerAvatar"
},
{
"scans": 9999,
"type": "/Lotus/Types/Game/CrewShip/GrineerDestroyer/Saturn/GrineerSaturnDestroyerAvatar"
},
{
"scans": 9999,
"type": "/Lotus/Types/Game/CrewShip/GrineerDestroyer/GrineerDestroyerAvatar"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -278,7 +278,8 @@
<div class="card mb-4">
<h5 class="card-header">Account</h5>
<div class="card-body">
<button class="btn btn-primary" onclick="doUnlockAllFocusSchools();">Unlock All Focus Schools</button>
<p><button class="btn btn-primary" onclick="doUnlockAllFocusSchools();">Unlock All Focus Schools</button></p>
<button class="btn btn-primary" onclick="doHelminthUnlockAll();">Fully Level Up Helminth</button>
</div>
</div>
</div>

View File

@ -956,6 +956,12 @@ function unlockFocusSchool(upgradeType) {
});
}
function doHelminthUnlockAll() {
revalidateAuthz(() => {
$.post("/api/infestedFoundry.php?" + window.authz + "&mode=custom_unlockall");
});
}
// Powersuit Route
single.getRoute("#powersuit-route").on("beforeload", function () {