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

View File

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

View File

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

View File

@ -30,13 +30,11 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
recipe => recipe._id?.toString() === claimCompletedRecipeRequest.RecipeIds[0].$oid recipe => recipe._id?.toString() === claimCompletedRecipeRequest.RecipeIds[0].$oid
); );
if (!pendingRecipe) { 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}`); throw new Error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`);
} }
//check recipe is indeed ready to be completed //check recipe is indeed ready to be completed
// if (pendingRecipe.CompletionDate > new Date()) { // 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`); // 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); const recipe = getRecipe(pendingRecipe.ItemType);
if (!recipe) { 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()}`); 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)) ...(await updateCurrencyByAccountId(recipe.skipBuildTimePrice, true, accountId))
}; };
} }
res.json({ const inventory = await getInventory(accountId);
InventoryChanges: { InventoryChanges = {
...InventoryChanges, ...InventoryChanges,
...(await addItem(accountId, recipe.resultType, recipe.num)).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 { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
export const getCreditsController: RequestHandler = async (req, res) => { export const creditsController: RequestHandler = async (req, res) => {
let accountId; const accountId = await getAccountIdForRequest(req);
try {
accountId = await getAccountIdForRequest(req);
} catch (e) {
res.status(400).send("Log-in expired");
return;
}
const inventory = await getInventory(accountId); 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 { IMiscItem, TFocusPolarity, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { ExportFocusUpgrades } from "warframe-public-export-plus"; import { ExportFocusUpgrades } from "warframe-public-export-plus";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
export const focusController: RequestHandler = async (req, res) => { export const focusController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); 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/SentAmpTrainingChassis",
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel" "/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
]; ];
const result = await addEquipment("OperatorAmps", request.StartingWeaponType, accountId, parts); const inventory = await getInventory(accountId);
res.json(result); const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, parts);
await inventory.save();
res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]);
break; break;
} }
case FocusOperation.UnbindUpgrade: { case FocusOperation.UnbindUpgrade: {

View File

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

View File

@ -2,7 +2,7 @@ import { RequestHandler } from "express";
import { getGuildForRequestEx } from "@/src/services/guildService"; import { getGuildForRequestEx } from "@/src/services/guildService";
import { ExportDojoRecipes } from "warframe-public-export-plus"; import { ExportDojoRecipes } from "warframe-public-export-plus";
import { getAccountIdForRequest } from "@/src/services/loginService"; 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 { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes";
@ -11,11 +11,12 @@ export const guildTechController: RequestHandler = async (req, res) => {
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
const guild = await getGuildForRequestEx(req, inventory); const guild = await getGuildForRequestEx(req, inventory);
const data = JSON.parse(String(req.body)) as TGuildTechRequest; const data = JSON.parse(String(req.body)) as TGuildTechRequest;
if (data.Action == "Sync") { const action = data.Action.split(",")[0];
if (action == "Sync") {
res.json({ res.json({
TechProjects: guild.toJSON().TechProjects TechProjects: guild.toJSON().TechProjects
}); });
} else if (data.Action == "Start") { } else if (action == "Start") {
const recipe = ExportDojoRecipes.research[data.RecipeType!]; const recipe = ExportDojoRecipes.research[data.RecipeType!];
guild.TechProjects ??= []; guild.TechProjects ??= [];
if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) { if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) {
@ -31,7 +32,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
} }
await guild.save(); await guild.save();
res.end(); res.end();
} else if (data.Action == "Contribute") { } else if (action == "Contribute") {
const contributions = data as IGuildTechContributeFields; const contributions = data as IGuildTechContributeFields;
const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!; const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
if (contributions.RegularCredits > techProject.ReqCredits) { if (contributions.RegularCredits > techProject.ReqCredits) {
@ -70,6 +71,30 @@ export const guildTechController: RequestHandler = async (req, res) => {
res.json({ res.json({
InventoryChanges: inventoryChanges 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 { } else {
throw new Error(`unknown guildTech action: ${data.Action}`); throw new Error(`unknown guildTech action: ${data.Action}`);
} }
@ -85,6 +110,8 @@ interface IGuildTechStartFields {
RecipeType: string; RecipeType: string;
} }
type IGuildTechBuyFields = IGuildTechStartFields;
interface IGuildTechContributeFields { interface IGuildTechContributeFields {
ResearchId: ""; ResearchId: "";
RecipeType: string; RecipeType: string;

View File

@ -3,15 +3,12 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory, addMiscItems, updateCurrency, addRecipes } from "@/src/services/inventoryService"; import { getInventory, addMiscItems, updateCurrency, addRecipes } from "@/src/services/inventoryService";
import { IOid } from "@/src/types/commonTypes"; import { IOid } from "@/src/types/commonTypes";
import { import { IConsumedSuit, IInfestedFoundry, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
IConsumedSuit,
IInfestedFoundry,
IInventoryDatabaseDocument,
IMiscItem,
ITypeCount
} from "@/src/types/inventoryTypes/inventoryTypes";
import { ExportMisc, ExportRecipes } from "warframe-public-export-plus"; import { ExportMisc, ExportRecipes } from "warframe-public-export-plus";
import { getRecipe } from "@/src/services/itemDataService"; import { getRecipe } from "@/src/services/itemDataService";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
import { logger } from "@/src/utils/logger";
export const infestedFoundryController: RequestHandler = async (req, res) => { export const infestedFoundryController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -44,6 +41,25 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
break; 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": { case "n": {
// name the beast // name the beast
const request = getJSONfromString(String(req.body)) as IHelminthNameRequest; const request = getJSONfromString(String(req.body)) as IHelminthNameRequest;
@ -71,21 +87,43 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
const miscItemChanges: IMiscItem[] = []; const miscItemChanges: IMiscItem[] = [];
let totalPercentagePointsGained = 0; let totalPercentagePointsGained = 0;
const currentUnixSeconds = Math.trunc(new Date().getTime() / 1000);
for (const contribution of request.ResourceContributions) { for (const contribution of request.ResourceContributions) {
const snack = ExportMisc.helminthSnacks[contribution.ItemType]; const snack = ExportMisc.helminthSnacks[contribution.ItemType];
// Note: Currently ignoring loss of apetite let resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type);
totalPercentagePointsGained += snack.gain / 0.01; if (!resource) {
const resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type); resource =
if (resource) { inventory.InfestedFoundry.Resources[
resource.Count += Math.trunc(snack.gain * 1000); inventory.InfestedFoundry.Resources.push({ ItemType: snack.type, Count: 0 }) - 1
} else { ];
inventory.InfestedFoundry.Resources.push({
ItemType: snack.type,
Count: Math.trunc(snack.gain * 1000)
});
} }
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 // tally items for removal
const change = miscItemChanges.find(x => x.ItemType == contribution.ItemType); const change = miscItemChanges.find(x => x.ItemType == contribution.ItemType);
if (change) { 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); addMiscItems(inventory, miscItemChanges);
await inventory.save(); await inventory.save();
res.json({ res.json({
InventoryChanges: { InventoryChanges: {
Recipes: recipeChanges,
InfestedFoundry: { InfestedFoundry: {
XP: inventory.InfestedFoundry.XP, XP: inventory.InfestedFoundry.XP,
Resources: inventory.InfestedFoundry.Resources, Resources: inventory.InfestedFoundry.Resources,
Slots: inventory.InfestedFoundry.Slots Slots: inventory.InfestedFoundry.Slots
} },
}, MiscItems: miscItemChanges
MiscItems: miscItemChanges }
}); });
break; break;
} }
case "o": // offerings update case "o": {
// {"OfferingsIndex":540,"SuitTypes":["/Lotus/Powersuits/PaxDuviricus/PaxDuviricusBaseSuit","/Lotus/Powersuits/Nezha/NezhaBaseSuit","/Lotus/Powersuits/Devourer/DevourerBaseSuit"],"Extra":false} // offerings update
res.status(404).end(); 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; break;
}
case "a": { case "a": {
// subsume warframe // subsume warframe
@ -134,18 +188,21 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
if (suit.Configs && suit.Configs[0] && suit.Configs[0].pricol) { if (suit.Configs && suit.Configs[0] && suit.Configs[0].pricol) {
consumedSuit.c = 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 ??= [];
inventory.InfestedFoundry!.ConsumedSuits?.push(consumedSuit); inventory.InfestedFoundry!.ConsumedSuits?.push(consumedSuit);
inventory.InfestedFoundry!.LastConsumedSuit = suit; inventory.InfestedFoundry!.LastConsumedSuit = suit;
inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = new Date( inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = new Date(
new Date().getTime() + 24 * 60 * 60 * 1000 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(); await inventory.save();
console.log(inventory.toJSON().InfestedFoundry);
res.json({ res.json({
InventoryChanges: { InventoryChanges: {
Recipes: recipeChanges,
RemovedIdItems: [ RemovedIdItems: [
{ {
ItemId: request.SuitId ItemId: request.SuitId
@ -178,6 +235,52 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
break; 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: default:
throw new Error(`unhandled infestedFoundry mode: ${String(req.query.mode)}`); throw new Error(`unhandled infestedFoundry mode: ${String(req.query.mode)}`);
} }
@ -190,6 +293,11 @@ interface IShardInstallRequest {
Color: string; Color: string;
} }
interface IShardUninstallRequest {
SuitId: IOid;
Slot: number;
}
interface IHelminthNameRequest { interface IHelminthNameRequest {
newName: string; newName: string;
} }
@ -216,7 +324,8 @@ const colorToShard: Record<string, string> = {
ACC_PURPLE_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalVioletMythic" 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; infestedFoundry.XP ??= 0;
const prevXP = infestedFoundry.XP; const prevXP = infestedFoundry.XP;
infestedFoundry.XP += delta; infestedFoundry.XP += delta;
@ -224,14 +333,69 @@ const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number):
infestedFoundry.Slots ??= 0; infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 3; 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) { if (prevXP < 15750_00 && infestedFoundry.XP >= 15750_00) {
infestedFoundry.Slots ??= 0; infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 10; 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) { if (prevXP < 39375_00 && infestedFoundry.XP >= 39375_00) {
infestedFoundry.Slots ??= 0; infestedFoundry.Slots ??= 0;
infestedFoundry.Slots += 20; infestedFoundry.Slots += 20;
} }
if (prevXP < 60750_00 && infestedFoundry.XP >= 60750_00) {
recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStatusBlueprint", ItemCount: 1 });
}
if (prevXP < 73125_00 && infestedFoundry.XP >= 73125_00) {
infestedFoundry.Slots = 1;
}
if (prevXP < 86625_00 && infestedFoundry.XP >= 86625_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldArmorBlueprint",
ItemCount: 1
});
}
if (prevXP < 101250_00 && infestedFoundry.XP >= 101250_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthProcBlockBlueprint",
ItemCount: 1
});
}
if (prevXP < 117000_00 && infestedFoundry.XP >= 117000_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthEnergyShareBlueprint",
ItemCount: 1
});
}
if (prevXP < 133875_00 && infestedFoundry.XP >= 133875_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthMaxStatusBlueprint",
ItemCount: 1
});
}
if (prevXP < 151875_00 && infestedFoundry.XP >= 151875_00) {
recipeChanges.push({
ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthTreasureBlueprint",
ItemCount: 1
});
}
return recipeChanges;
}; };
interface IHelminthSubsumeRequest { interface IHelminthSubsumeRequest {
@ -239,7 +403,7 @@ interface IHelminthSubsumeRequest {
Recipe: string; Recipe: string;
} }
export const handleSubsumeCompletion = (inventory: IInventoryDatabaseDocument): ITypeCount[] => { export const handleSubsumeCompletion = (inventory: TInventoryDatabaseDocument): ITypeCount[] => {
const [recipeType] = Object.entries(ExportRecipes).find( const [recipeType] = Object.entries(ExportRecipes).find(
([_recipeType, recipe]) => ([_recipeType, recipe]) =>
recipe.secretIngredientAction == "SIA_WARFRAME_ABILITY" && recipe.secretIngredientAction == "SIA_WARFRAME_ABILITY" &&
@ -256,3 +420,52 @@ export const handleSubsumeCompletion = (inventory: IInventoryDatabaseDocument):
addRecipes(inventory, recipeChanges); addRecipes(inventory, recipeChanges);
return 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 { RequestHandler } from "express";
import { getAccountForRequest } from "@/src/services/loginService"; import { getAccountForRequest } from "@/src/services/loginService";
import { toInventoryResponse } from "@/src/helpers/inventoryHelpers";
import { Inventory } from "@/src/models/inventoryModels/inventoryModel"; import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
import allDialogue from "@/static/fixed_responses/allDialogue.json"; import allDialogue from "@/static/fixed_responses/allDialogue.json";
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes"; 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 { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { import {
ExportCustoms, ExportCustoms,
@ -18,17 +17,9 @@ import {
import { handleSubsumeCompletion } from "./infestedFoundryController"; import { handleSubsumeCompletion } from "./infestedFoundryController";
export const inventoryController: RequestHandler = async (request, response) => { export const inventoryController: RequestHandler = async (request, response) => {
let account; const account = await getAccountForRequest(request);
try {
account = await getAccountForRequest(request);
} catch (e) {
response.status(400).send("Log-in expired");
return;
}
const inventory = await Inventory.findOne({ accountOwnerId: account._id.toString() }) const inventory = await Inventory.findOne({ accountOwnerId: account._id.toString() });
.populate<{ LoadOutPresets: ILoadoutDatabase }>("LoadOutPresets")
.populate<{ Ships: IShipInventory }>("Ships");
if (!inventory) { if (!inventory) {
response.status(400).json({ error: "inventory was undefined" }); response.status(400).json({ error: "inventory was undefined" });
@ -41,20 +32,21 @@ export const inventoryController: RequestHandler = async (request, response) =>
account.LastLoginDay = today; account.LastLoginDay = today;
await account.save(); await account.save();
inventory.DailyAffiliation = 16000; inventory.DailyAffiliation = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationPvp = 16000; inventory.DailyAffiliationPvp = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationLibrary = 16000; inventory.DailyAffiliationLibrary = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationCetus = 16000; inventory.DailyAffiliationCetus = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationQuills = 16000; inventory.DailyAffiliationQuills = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationSolaris = 16000; inventory.DailyAffiliationSolaris = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationVentkids = 16000; inventory.DailyAffiliationVentkids = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationVox = 16000; inventory.DailyAffiliationVox = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationEntrati = 16000; inventory.DailyAffiliationEntrati = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationNecraloid = 16000; inventory.DailyAffiliationNecraloid = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationZariman = 16000; inventory.DailyAffiliationZariman = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationKahl = 16000; inventory.DailyAffiliationKahl = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationCavia = 16000; inventory.DailyAffiliationCavia = 16000 + inventory.PlayerLevel * 500;
inventory.DailyAffiliationHex = 16000; inventory.DailyAffiliationHex = 16000 + inventory.PlayerLevel * 500;
inventory.DailyFocus = 250000 + inventory.PlayerLevel * 5000;
await inventory.save(); await inventory.save();
} }
@ -63,14 +55,17 @@ export const inventoryController: RequestHandler = async (request, response) =>
inventory.InfestedFoundry.AbilityOverrideUnlockCooldown && inventory.InfestedFoundry.AbilityOverrideUnlockCooldown &&
new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown
) { ) {
handleSubsumeCompletion(inventory as unknown as IInventoryDatabaseDocument); handleSubsumeCompletion(inventory);
await inventory.save(); await inventory.save();
} }
//TODO: make a function that converts from database representation to client const inventoryWithLoadOutPresets = await inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>(
const inventoryJSON = inventory.toJSON(); "LoadOutPresets"
);
const inventoryResponse = toInventoryResponse(inventoryJSON); const inventoryWithLoadOutPresetsAndShips = await inventoryWithLoadOutPresets.populate<{ Ships: IShipInventory }>(
"Ships"
);
const inventoryResponse = inventoryWithLoadOutPresetsAndShips.toJSON<IInventoryResponse>();
if (config.infiniteCredits) { if (config.infiniteCredits) {
inventoryResponse.RegularCredits = 999999999; inventoryResponse.RegularCredits = 999999999;

View File

@ -1,5 +1,5 @@
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { updateCurrencyByAccountId } from "@/src/services/inventoryService"; import { getInventory, updateCurrency } from "@/src/services/inventoryService";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { updateSlots } from "@/src/services/inventoryService"; import { updateSlots } from "@/src/services/inventoryService";
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; 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 //TODO: check which slot was purchased because pvpBonus is also possible
const currencyChanges = await updateCurrencyByAccountId(20, true, accountId); const inventory = await getInventory(accountId);
await updateSlots(accountId, InventorySlot.PVE_LOADOUTS, 1, 1); const currencyChanges = updateCurrency(inventory, 20, true);
updateSlots(inventory, InventorySlot.PVE_LOADOUTS, 1, 1);
await inventory.save();
//console.log({ InventoryChanges: currencyChanges }, " added loadout changes:"); //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}`); throw new Error(`unknown modular weapon type: ${data.WeaponType}`);
} }
const category = modularWeaponTypes[data.WeaponType]; const category = modularWeaponTypes[data.WeaponType];
const inventory = await getInventory(accountId);
// Give weapon // Give weapon
const weapon = await addEquipment(category, data.WeaponType, accountId, data.Parts); const weapon = addEquipment(inventory, category, data.WeaponType, data.Parts);
// Remove credits & parts // Remove credits & parts
const miscItemChanges = []; const miscItemChanges = [];
@ -46,7 +47,6 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
ItemCount: -1 ItemCount: -1
}); });
} }
const inventory = await getInventory(accountId);
const currencyChanges = updateCurrency( const currencyChanges = updateCurrency(
inventory, inventory,
category == "Hoverboards" || category == "MoaPets" ? 5000 : 4000, category == "Hoverboards" || category == "MoaPets" ? 5000 : 4000,

View File

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

View File

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

View File

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

View File

@ -37,9 +37,20 @@ const getItemListsController: RequestHandler = (req, res) => {
} }
} }
for (const [uniqueName, item] of Object.entries(ExportResources)) { 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({ miscitems.push({
uniqueName: item.productCategory + ":" + uniqueName, uniqueName: item.productCategory + ":" + uniqueName,
name: getString(item.name, lang) name: name
}); });
} }
for (const [uniqueName, item] of Object.entries(ExportGear)) { 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 { IMongoDate, IOid } from "@/src/types/commonTypes";
import { IInventoryResponse } from "@/src/types/inventoryTypes/inventoryTypes";
import { Types } from "mongoose"; 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 => { export const toOid = (objectId: Types.ObjectId): IOid => {
return { $oid: objectId.toString() } satisfies 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 { import {
IFlavourItem, IFlavourItem,
IRawUpgrade, IRawUpgrade,
@ -45,7 +45,8 @@ import {
ICrewShipMembers, ICrewShipMembers,
ICrewShip, ICrewShip,
ICrewShipPilotWeapon, ICrewShipPilotWeapon,
IShipExterior IShipExterior,
IHelminthFoodRecord
} from "../../types/inventoryTypes/inventoryTypes"; } from "../../types/inventoryTypes/inventoryTypes";
import { IOid } from "../../types/commonTypes"; import { IOid } from "../../types/commonTypes";
import { import {
@ -233,6 +234,9 @@ const EquipmentSchema = new Schema<IEquipmentDatabase>(
UnlockLevel: Number, UnlockLevel: Number,
Expiry: Date, Expiry: Date,
SkillTree: String, SkillTree: String,
OffensiveUpgrade: String,
DefensiveUpgrade: String,
UpgradesExpiry: Date,
ArchonCrystalUpgrades: { type: [ArchonCrystalUpgradeSchema], default: undefined } ArchonCrystalUpgrades: { type: [ArchonCrystalUpgradeSchema], default: undefined }
}, },
{ id: false } { id: false }
@ -467,7 +471,22 @@ const consumedSchuitsSchema = new Schema<IConsumedSuit>(
{ _id: false } { _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>( const infestedFoundrySchema = new Schema<IInfestedFoundry>(
{ {
@ -1061,6 +1080,7 @@ inventorySchema.set("toJSON", {
transform(_document, returnedObject) { transform(_document, returnedObject) {
delete returnedObject._id; delete returnedObject._id;
delete returnedObject.__v; delete returnedObject.__v;
delete returnedObject.accountOwnerId;
const inventoryDatabase = returnedObject as IInventoryDatabase; const inventoryDatabase = returnedObject as IInventoryDatabase;
const inventoryResponse = returnedObject as IInventoryResponse; const inventoryResponse = returnedObject as IInventoryResponse;
@ -1114,3 +1134,15 @@ type InventoryDocumentProps = {
type InventoryModelType = Model<IInventoryDatabase, {}, InventoryDocumentProps>; type InventoryModelType = Model<IInventoryDatabase, {}, InventoryDocumentProps>;
export const Inventory = model<IInventoryDatabase, InventoryModelType>("Inventory", inventorySchema); 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 { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController"; import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
import { createGuildController } from "@/src/controllers/api/createGuildController"; import { createGuildController } from "@/src/controllers/api/createGuildController";
import { creditsController } from "@/src/controllers/api/creditsController";
import { deleteSessionController } from "@/src/controllers/api/deleteSessionController"; import { deleteSessionController } from "@/src/controllers/api/deleteSessionController";
import { dojoController } from "@/src/controllers/api/dojoController"; import { dojoController } from "@/src/controllers/api/dojoController";
import { dronesController } from "@/src/controllers/api/dronesController"; 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 { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
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 { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController"; 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 { getGuildController } from "@/src/controllers/api/getGuildController";
@ -76,7 +76,7 @@ const apiRouter = express.Router();
// get // get
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController); apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
apiRouter.get("/credits.php", getCreditsController); apiRouter.get("/credits.php", creditsController);
apiRouter.get("/deleteSession.php", deleteSessionController); apiRouter.get("/deleteSession.php", deleteSessionController);
apiRouter.get("/dojo", dojoController); apiRouter.get("/dojo", dojoController);
apiRouter.get("/drones.php", dronesController); 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 { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService"; import { getInventory } from "@/src/services/inventoryService";
import { Guild } from "@/src/models/guildModel"; 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) => { export const getGuildForRequest = async (req: Request) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
@ -10,7 +10,7 @@ export const getGuildForRequest = async (req: Request) => {
return await getGuildForRequestEx(req, inventory); 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; const guildId = req.query.guildId as string;
if (!inventory.GuildId || inventory.GuildId.toString() != guildId) { if (!inventory.GuildId || inventory.GuildId.toString() != guildId) {
throw new Error("Account is not in the guild that it has sent a request for"); 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 postTutorialInventory from "@/static/fixed_responses/postTutorialInventory.json";
import { config } from "@/src/services/configService"; import { config } from "@/src/services/configService";
import { Types } from "mongoose"; import { Types } from "mongoose";
@ -7,7 +7,6 @@ import {
IChallengeProgress, IChallengeProgress,
IConsumable, IConsumable,
IFlavourItem, IFlavourItem,
IInventoryDatabaseDocument,
IMiscItem, IMiscItem,
IMission, IMission,
IRawUpgrade, IRawUpgrade,
@ -80,8 +79,9 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
} }
} else if (typeof delta[key] == "object") { } else if (typeof delta[key] == "object") {
console.assert(key.substring(-3) == "Bin"); console.assert(key.substring(-3) == "Bin");
console.assert(key != "InfestedFoundry");
const left = InventoryChanges[key] as IBinChanges; const left = InventoryChanges[key] as IBinChanges;
const right: IBinChanges = delta[key]; const right = delta[key] as IBinChanges;
left.count += right.count; left.count += right.count;
left.platinum += right.platinum; left.platinum += right.platinum;
left.Slots += right.Slots; 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 }); const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId });
if (!inventory) { if (!inventory) {
@ -106,13 +106,12 @@ export const getInventory = async (accountOwnerId: string) => {
}; };
export const addItem = async ( export const addItem = async (
accountId: string, inventory: TInventoryDatabaseDocument,
typeName: string, typeName: string,
quantity: number = 1 quantity: number = 1
): Promise<{ InventoryChanges: IInventoryChanges }> => { ): Promise<{ InventoryChanges: IInventoryChanges }> => {
// Strict typing // Strict typing
if (typeName in ExportRecipes) { if (typeName in ExportRecipes) {
const inventory = await getInventory(accountId);
const recipeChanges = [ const recipeChanges = [
{ {
ItemType: typeName, ItemType: typeName,
@ -120,7 +119,6 @@ export const addItem = async (
} satisfies ITypeCount } satisfies ITypeCount
]; ];
addRecipes(inventory, recipeChanges); addRecipes(inventory, recipeChanges);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
Recipes: recipeChanges Recipes: recipeChanges
@ -128,11 +126,9 @@ export const addItem = async (
}; };
} }
if (typeName in ExportResources) { if (typeName in ExportResources) {
const inventory = await getInventory(accountId);
if (ExportResources[typeName].productCategory == "Ships") { 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); inventory.Ships.push(oid);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
Ships: [ Ships: [
@ -144,11 +140,8 @@ export const addItem = async (
} }
}; };
} else if (ExportResources[typeName].productCategory == "CrewShips") { } else if (ExportResources[typeName].productCategory == "CrewShips") {
return { const inventoryChanges = addCrewShip(inventory, typeName);
InventoryChanges: { return { InventoryChanges: inventoryChanges };
CrewShips: [await addCrewShip(typeName, accountId)]
}
};
} else { } else {
const miscItemChanges = [ const miscItemChanges = [
{ {
@ -157,7 +150,6 @@ export const addItem = async (
} satisfies IMiscItem } satisfies IMiscItem
]; ];
addMiscItems(inventory, miscItemChanges); addMiscItems(inventory, miscItemChanges);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
MiscItems: miscItemChanges MiscItems: miscItemChanges
@ -166,21 +158,14 @@ export const addItem = async (
} }
} }
if (typeName in ExportCustoms) { if (typeName in ExportCustoms) {
return { const inventoryChanges = addSkin(inventory, typeName);
InventoryChanges: { return { InventoryChanges: inventoryChanges };
WeaponSkins: [await addSkin(typeName, accountId)]
}
};
} }
if (typeName in ExportFlavour) { if (typeName in ExportFlavour) {
return { const inventoryChanges = addCustomization(inventory, typeName);
InventoryChanges: { return { InventoryChanges: inventoryChanges };
FlavourItems: [await addCustomization(typeName, accountId)]
}
};
} }
if (typeName in ExportUpgrades || typeName in ExportArcanes) { if (typeName in ExportUpgrades || typeName in ExportArcanes) {
const inventory = await getInventory(accountId);
const changes = [ const changes = [
{ {
ItemType: typeName, ItemType: typeName,
@ -188,7 +173,6 @@ export const addItem = async (
} }
]; ];
addMods(inventory, changes); addMods(inventory, changes);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
RawUpgrades: changes RawUpgrades: changes
@ -196,7 +180,6 @@ export const addItem = async (
}; };
} }
if (typeName in ExportGear) { if (typeName in ExportGear) {
const inventory = await getInventory(accountId);
const consumablesChanges = [ const consumablesChanges = [
{ {
ItemType: typeName, ItemType: typeName,
@ -204,7 +187,6 @@ export const addItem = async (
} satisfies IConsumable } satisfies IConsumable
]; ];
addConsumables(inventory, consumablesChanges); addConsumables(inventory, consumablesChanges);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
Consumables: consumablesChanges Consumables: consumablesChanges
@ -217,8 +199,8 @@ export const addItem = async (
case "Powersuits": case "Powersuits":
switch (typeName.substr(1).split("/")[2]) { switch (typeName.substr(1).split("/")[2]) {
default: { default: {
const inventoryChanges = await addPowerSuit(typeName, accountId); const inventoryChanges = addPowerSuit(inventory, typeName);
await updateSlots(accountId, InventorySlot.SUITS, 0, 1); updateSlots(inventory, InventorySlot.SUITS, 0, 1);
return { return {
InventoryChanges: { InventoryChanges: {
...inventoryChanges, ...inventoryChanges,
@ -231,22 +213,22 @@ export const addItem = async (
}; };
} }
case "Archwing": { case "Archwing": {
const spaceSuit = await addSpaceSuit(typeName, accountId); const inventoryChanges = addSpaceSuit(inventory, typeName);
await updateSlots(accountId, InventorySlot.SPACESUITS, 0, 1); updateSlots(inventory, InventorySlot.SPACESUITS, 0, 1);
return { return {
InventoryChanges: { InventoryChanges: {
...inventoryChanges,
SpaceSuitBin: { SpaceSuitBin: {
count: 1, count: 1,
platinum: 0, platinum: 0,
Slots: -1 Slots: -1
}, }
SpaceSuits: [spaceSuit]
} }
}; };
} }
case "EntratiMech": { case "EntratiMech": {
const inventoryChanges = await addMechSuit(typeName, accountId); const inventoryChanges = addMechSuit(inventory, typeName);
await updateSlots(accountId, InventorySlot.MECHSUITS, 0, 1); updateSlots(inventory, InventorySlot.MECHSUITS, 0, 1);
return { return {
InventoryChanges: { InventoryChanges: {
...inventoryChanges, ...inventoryChanges,
@ -262,18 +244,17 @@ export const addItem = async (
break; break;
case "Weapons": { case "Weapons": {
const weaponType = getWeaponType(typeName); const weaponType = getWeaponType(typeName);
const weapon = await addEquipment(weaponType, typeName, accountId); const inventoryChanges = addEquipment(inventory, weaponType, typeName);
await updateSlots(accountId, InventorySlot.WEAPONS, 0, 1); updateSlots(inventory, InventorySlot.WEAPONS, 0, 1);
return { return {
InventoryChanges: { InventoryChanges: {
WeaponBin: { count: 1, platinum: 0, Slots: -1 }, ...inventoryChanges,
[weaponType]: [weapon] WeaponBin: { count: 1, platinum: 0, Slots: -1 }
} }
}; };
} }
case "Objects": { case "Objects": {
// /Lotus/Objects/Tenno/Props/TnoLisetTextProjector (Note Beacon) // /Lotus/Objects/Tenno/Props/TnoLisetTextProjector (Note Beacon)
const inventory = await getInventory(accountId);
const changes = [ const changes = [
{ {
ItemType: typeName, ItemType: typeName,
@ -281,7 +262,6 @@ export const addItem = async (
} satisfies IMiscItem } satisfies IMiscItem
]; ];
addShipDecorations(inventory, changes); addShipDecorations(inventory, changes);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
ShipDecorations: changes ShipDecorations: changes
@ -291,8 +271,8 @@ export const addItem = async (
case "Types": case "Types":
switch (typeName.substr(1).split("/")[2]) { switch (typeName.substr(1).split("/")[2]) {
case "Sentinels": { case "Sentinels": {
const inventoryChanges = await addSentinel(typeName, accountId); const inventoryChanges = addSentinel(inventory, typeName);
await updateSlots(accountId, InventorySlot.SENTINELS, 0, 1); updateSlots(inventory, InventorySlot.SENTINELS, 0, 1);
return { return {
InventoryChanges: { InventoryChanges: {
...inventoryChanges, ...inventoryChanges,
@ -303,7 +283,6 @@ export const addItem = async (
case "Items": { case "Items": {
switch (typeName.substr(1).split("/")[3]) { switch (typeName.substr(1).split("/")[3]) {
case "ShipDecos": { case "ShipDecos": {
const inventory = await getInventory(accountId);
const changes = [ const changes = [
{ {
ItemType: typeName, ItemType: typeName,
@ -311,7 +290,6 @@ export const addItem = async (
} satisfies IMiscItem } satisfies IMiscItem
]; ];
addShipDecorations(inventory, changes); addShipDecorations(inventory, changes);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
ShipDecorations: changes ShipDecorations: changes
@ -319,7 +297,6 @@ export const addItem = async (
}; };
} }
default: { default: {
const inventory = await getInventory(accountId);
const miscItemChanges = [ const miscItemChanges = [
{ {
ItemType: typeName, ItemType: typeName,
@ -327,7 +304,6 @@ export const addItem = async (
} satisfies IMiscItem } satisfies IMiscItem
]; ];
addMiscItems(inventory, miscItemChanges); addMiscItems(inventory, miscItemChanges);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
MiscItems: miscItemChanges MiscItems: miscItemChanges
@ -339,7 +315,6 @@ export const addItem = async (
case "Game": case "Game":
if (typeName.substr(1).split("/")[3] == "Projections") { if (typeName.substr(1).split("/")[3] == "Projections") {
// Void Relics, e.g. /Lotus/Types/Game/Projections/T2VoidProjectionGaussPrimeDBronze // Void Relics, e.g. /Lotus/Types/Game/Projections/T2VoidProjectionGaussPrimeDBronze
const inventory = await getInventory(accountId);
const miscItemChanges = [ const miscItemChanges = [
{ {
ItemType: typeName, ItemType: typeName,
@ -347,7 +322,6 @@ export const addItem = async (
} satisfies IMiscItem } satisfies IMiscItem
]; ];
addMiscItems(inventory, miscItemChanges); addMiscItems(inventory, miscItemChanges);
await inventory.save();
return { return {
InventoryChanges: { InventoryChanges: {
MiscItems: miscItemChanges MiscItems: miscItemChanges
@ -364,13 +338,13 @@ export const addItem = async (
}; };
//TODO: maybe genericMethod for all the add methods, they share a lot of logic //TODO: maybe genericMethod for all the add methods, they share a lot of logic
export const addSentinel = async (sentinelName: string, accountId: string): Promise<IInventoryChanges> => { export const addSentinel = (
const inventoryChanges: IInventoryChanges = {}; inventory: TInventoryDatabaseDocument,
sentinelName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
if (ExportSentinels[sentinelName]?.defaultWeapon) { if (ExportSentinels[sentinelName]?.defaultWeapon) {
inventoryChanges.SentinelWeapons = [ addSentinelWeapon(inventory, ExportSentinels[sentinelName].defaultWeapon, inventoryChanges);
await addSentinelWeapon(ExportSentinels[sentinelName].defaultWeapon, accountId)
];
} }
const modsToGive: IRawUpgrade[] = []; const modsToGive: IRawUpgrade[] = [];
@ -388,96 +362,109 @@ export const addSentinel = async (sentinelName: string, accountId: string): Prom
} }
} }
const inventory = await getInventory(accountId);
addMods(inventory, modsToGive); addMods(inventory, modsToGive);
const sentinelIndex = inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0 }); const sentinelIndex = inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0 }) - 1;
const changedInventory = await inventory.save(); inventoryChanges.Sentinels ??= [];
inventoryChanges.Sentinels = [changedInventory.Sentinels[sentinelIndex - 1].toJSON()]; (inventoryChanges.Sentinels as IEquipmentClient[]).push(
inventory.Sentinels[sentinelIndex].toJSON<IEquipmentClient>()
);
return inventoryChanges; return inventoryChanges;
}; };
export const addSentinelWeapon = async (typeName: string, accountId: string): Promise<IEquipmentClient> => { export const addSentinelWeapon = (
const inventory = await getInventory(accountId); inventory: TInventoryDatabaseDocument,
const sentinelIndex = inventory.SentinelWeapons.push({ ItemType: typeName }); typeName: string,
const changedInventory = await inventory.save(); inventoryChanges: IInventoryChanges
return changedInventory.SentinelWeapons[sentinelIndex - 1].toJSON<IEquipmentClient>(); ): 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> => { export const addPowerSuit = (
const inventoryChanges: IInventoryChanges = {}; inventory: TInventoryDatabaseDocument,
powersuitName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const specialItems = getExalted(powersuitName); const specialItems = getExalted(powersuitName);
if (specialItems) { if (specialItems) {
for await (const specialItem of specialItems) { for (const specialItem of specialItems) {
await addSpecialItem(specialItem, accountId, inventoryChanges); addSpecialItem(inventory, specialItem, inventoryChanges);
} }
} }
const inventory = await getInventory(accountId); const suitIndex = inventory.Suits.push({ ItemType: powersuitName, Configs: [], UpgradeVer: 101, XP: 0 }) - 1;
const suitIndex = inventory.Suits.push({ ItemType: powersuitName, Configs: [], UpgradeVer: 101, XP: 0 }); inventoryChanges.Suits ??= [];
const changedInventory = await inventory.save(); (inventoryChanges.Suits as IEquipmentClient[]).push(inventory.Suits[suitIndex].toJSON<IEquipmentClient>());
inventoryChanges.Suits = [changedInventory.Suits[suitIndex - 1].toJSON()];
return inventoryChanges; return inventoryChanges;
}; };
export const addMechSuit = async (mechsuitName: string, accountId: string): Promise<IInventoryChanges> => { export const addMechSuit = (
const inventoryChanges: IInventoryChanges = {}; inventory: TInventoryDatabaseDocument,
mechsuitName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const specialItems = getExalted(mechsuitName); const specialItems = getExalted(mechsuitName);
if (specialItems) { if (specialItems) {
for await (const specialItem of specialItems) { for (const specialItem of specialItems) {
await addSpecialItem(specialItem, accountId, inventoryChanges); addSpecialItem(inventory, specialItem, inventoryChanges);
} }
} }
const inventory = await getInventory(accountId); const suitIndex = inventory.MechSuits.push({ ItemType: mechsuitName, Configs: [], UpgradeVer: 101, XP: 0 }) - 1;
const suitIndex = inventory.MechSuits.push({ ItemType: mechsuitName, Configs: [], UpgradeVer: 101, XP: 0 }); inventoryChanges.MechSuits ??= [];
const changedInventory = await inventory.save(); (inventoryChanges.MechSuits as IEquipmentClient[]).push(inventory.MechSuits[suitIndex].toJSON<IEquipmentClient>());
inventoryChanges.MechSuits = [changedInventory.MechSuits[suitIndex - 1].toJSON()];
return inventoryChanges; return inventoryChanges;
}; };
export const addSpecialItem = async ( export const addSpecialItem = (
inventory: TInventoryDatabaseDocument,
itemName: string, itemName: string,
accountId: string,
inventoryChanges: IInventoryChanges inventoryChanges: IInventoryChanges
): Promise<void> => { ): void => {
const inventory = await getInventory(accountId);
if (inventory.SpecialItems.find(x => x.ItemType == itemName)) { if (inventory.SpecialItems.find(x => x.ItemType == itemName)) {
return; return;
} }
const specialItemIndex = inventory.SpecialItems.push({ const specialItemIndex =
ItemType: itemName, inventory.SpecialItems.push({
Configs: [], ItemType: itemName,
Features: 1, Configs: [],
UpgradeVer: 101, Features: 1,
XP: 0 UpgradeVer: 101,
}); XP: 0
const changedInventory = await inventory.save(); }) - 1;
inventoryChanges.SpecialItems ??= []; 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> => { export const addSpaceSuit = (
const inventory = await getInventory(accountId); inventory: TInventoryDatabaseDocument,
const suitIndex = inventory.SpaceSuits.push({ ItemType: spacesuitName, Configs: [], UpgradeVer: 101, XP: 0 }); spacesuitName: string,
const changedInventory = await inventory.save(); inventoryChanges: IInventoryChanges = {}
return changedInventory.SpaceSuits[suitIndex - 1].toJSON<IEquipmentClient>(); ): 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 ( export const updateSlots = (
accountId: string, inventory: TInventoryDatabaseDocument,
slotName: SlotNames, slotName: SlotNames,
slotAmount: number, slotAmount: number,
extraAmount: number extraAmount: number
): Promise<void> => { ): void => {
const inventory = await getInventory(accountId);
inventory[slotName].Slots += slotAmount; inventory[slotName].Slots += slotAmount;
if (inventory[slotName].Extra === undefined) { if (inventory[slotName].Extra === undefined) {
inventory[slotName].Extra = extraAmount; inventory[slotName].Extra = extraAmount;
} else { } else {
inventory[slotName].Extra += extraAmount; inventory[slotName].Extra += extraAmount;
} }
await inventory.save();
}; };
const isCurrencyTracked = (usePremium: boolean): boolean => { const isCurrencyTracked = (usePremium: boolean): boolean => {
@ -485,7 +472,7 @@ const isCurrencyTracked = (usePremium: boolean): boolean => {
}; };
export const updateCurrency = ( export const updateCurrency = (
inventory: IInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
price: number, price: number,
usePremium: boolean usePremium: boolean
): ICurrencyChanges => { ): ICurrencyChanges => {
@ -549,48 +536,65 @@ export const updateTheme = async (data: IThemeUpdateRequest, accountId: string):
await inventory.save(); await inventory.save();
}; };
export const addEquipment = async ( export const addEquipment = (
inventory: TInventoryDatabaseDocument,
category: TEquipmentKey, category: TEquipmentKey,
type: string, type: string,
accountId: string, modularParts: string[] | undefined = undefined,
modularParts: string[] | undefined = undefined inventoryChanges: IInventoryChanges = {}
): Promise<IEquipmentClient> => { ): IInventoryChanges => {
const inventory = await getInventory(accountId); const index =
inventory[category].push({
ItemType: type,
Configs: [],
XP: 0,
ModularParts: modularParts
}) - 1;
const index = inventory[category].push({ inventoryChanges[category] ??= [];
ItemType: type, (inventoryChanges[category] as IEquipmentClient[]).push(inventory[category][index].toJSON<IEquipmentClient>());
Configs: [], return inventoryChanges;
XP: 0,
ModularParts: modularParts
});
const changedInventory = await inventory.save();
return changedInventory[category][index - 1].toJSON() as object as IEquipmentClient;
}; };
export const addCustomization = async (customizatonName: string, accountId: string): Promise<IFlavourItem> => { export const addCustomization = (
const inventory = await getInventory(accountId); inventory: TInventoryDatabaseDocument,
const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizatonName }) - 1; customizationName: string,
const changedInventory = await inventory.save(); inventoryChanges: IInventoryChanges = {}
return changedInventory.FlavourItems[flavourItemIndex].toJSON(); ): 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> => { export const addSkin = (
const inventory = await getInventory(accountId); inventory: TInventoryDatabaseDocument,
typeName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const index = inventory.WeaponSkins.push({ ItemType: typeName }) - 1; const index = inventory.WeaponSkins.push({ ItemType: typeName }) - 1;
const changedInventory = await inventory.save(); inventoryChanges.WeaponSkins ??= [];
return changedInventory.WeaponSkins[index].toJSON() as object as IWeaponSkinClient; (inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push(
inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>()
);
return inventoryChanges;
}; };
const addCrewShip = async (typeName: string, accountId: string) => { const addCrewShip = (
const inventory = await getInventory(accountId); inventory: TInventoryDatabaseDocument,
typeName: string,
inventoryChanges: IInventoryChanges = {}
): IInventoryChanges => {
const index = inventory.CrewShips.push({ ItemType: typeName }) - 1; const index = inventory.CrewShips.push({ ItemType: typeName }) - 1;
const changedInventory = await inventory.save(); inventoryChanges.CrewShips ??= [];
return changedInventory.CrewShips[index].toJSON(); (inventoryChanges.CrewShips as object[]).push(inventory.CrewShips[index].toJSON());
return inventoryChanges;
}; };
const addGearExpByCategory = ( const addGearExpByCategory = (
inventory: IInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
gearArray: IEquipmentClient[] | undefined, gearArray: IEquipmentClient[] | undefined,
categoryName: TEquipmentKey categoryName: TEquipmentKey
): void => { ): 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; const { MiscItems } = inventory;
itemsArray?.forEach(({ ItemCount, ItemType }) => { itemsArray?.forEach(({ ItemCount, ItemType }) => {
@ -638,7 +642,7 @@ export const addMiscItems = (inventory: IInventoryDatabaseDocument, itemsArray:
}; };
export const addShipDecorations = ( export const addShipDecorations = (
inventory: IInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
itemsArray: IConsumable[] | undefined itemsArray: IConsumable[] | undefined
): void => { ): void => {
const { ShipDecorations } = inventory; 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; const { Consumables } = inventory;
itemsArray?.forEach(({ ItemCount, ItemType }) => { 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; const { Recipes } = inventory;
itemsArray?.forEach(({ ItemCount, ItemType }) => { 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; const { RawUpgrades } = inventory;
itemsArray?.forEach(({ ItemType, ItemCount }) => { itemsArray?.forEach(({ ItemType, ItemCount }) => {
const itemIndex = RawUpgrades.findIndex(i => i.ItemType === ItemType); const itemIndex = RawUpgrades.findIndex(i => i.ItemType === ItemType);
@ -700,7 +704,7 @@ export const addMods = (inventory: IInventoryDatabaseDocument, itemsArray: IRawU
}; };
export const addFusionTreasures = ( export const addFusionTreasures = (
inventory: IInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
itemsArray: IFusionTreasure[] | undefined itemsArray: IFusionTreasure[] | undefined
): void => { ): void => {
const { FusionTreasures } = inventory; const { FusionTreasures } = inventory;
@ -729,7 +733,7 @@ export const updateChallengeProgress = async (
}; };
export const addSeasonalChallengeHistory = ( export const addSeasonalChallengeHistory = (
inventory: IInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
itemsArray: ISeasonChallenge[] | undefined itemsArray: ISeasonChallenge[] | undefined
): void => { ): void => {
const category = inventory.SeasonChallengeHistory; const category = inventory.SeasonChallengeHistory;
@ -746,7 +750,7 @@ export const addSeasonalChallengeHistory = (
}; };
export const addChallenges = ( export const addChallenges = (
inventory: IInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
itemsArray: IChallengeProgress[] | undefined itemsArray: IChallengeProgress[] | undefined
): void => { ): void => {
const category = inventory.ChallengeProgress; 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 { Missions } = inventory;
const itemIndex = Missions.findIndex(item => item.Tag === Tag); 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; existingBooster.ExpiryDate = Math.max(existingBooster.ExpiryDate, currentTime) + time;
inventory.markModified(`Boosters.${itemIndex}.ExpiryDate`); inventory.markModified(`Boosters.${itemIndex}.ExpiryDate`);
} else { } else {
Boosters.push({ ItemType, ExpiryDate: currentTime + time }) - 1; Boosters.push({ ItemType, ExpiryDate: currentTime + time });
} }
await inventory.save(); await inventory.save();

View File

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

View File

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

View File

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

View File

@ -204,9 +204,12 @@ export const handleStoreItemAcquisition = async (
} }
} }
switch (storeCategory) { switch (storeCategory) {
default: default: {
purchaseResponse = await addItem(accountId, internalName, quantity); const inventory = await getInventory(accountId);
purchaseResponse = await addItem(inventory, internalName, quantity);
await inventory.save();
break; break;
}
case "Types": case "Types":
purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity); purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity);
break; break;
@ -237,7 +240,8 @@ export const slotPurchaseNameToSlotName: SlotPurchase = {
// // number of frames = extra - slots + 2 // // number of frames = extra - slots + 2
const handleSlotPurchase = async ( const handleSlotPurchase = async (
slotPurchaseNameFull: string, slotPurchaseNameFull: string,
accountId: string accountId: string,
quantity: number
): Promise<{ InventoryChanges: IInventoryChanges }> => { ): Promise<{ InventoryChanges: IInventoryChanges }> => {
logger.debug(`slot name ${slotPurchaseNameFull}`); logger.debug(`slot name ${slotPurchaseNameFull}`);
const slotPurchaseName = parseSlotPurchaseName( const slotPurchaseName = parseSlotPurchaseName(
@ -246,19 +250,21 @@ const handleSlotPurchase = async (
logger.debug(`slot purchase name ${slotPurchaseName}`); logger.debug(`slot purchase name ${slotPurchaseName}`);
const slotName = slotPurchaseNameToSlotName[slotPurchaseName].name; 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 { return {
InventoryChanges: { InventoryChanges: {
[slotName]: { [slotName]: {
count: 0, count: 0,
platinum: 1, platinum: 1,
Slots: slotsPerPurchase, Slots: slotsPurchased,
Extra: slotsPerPurchase Extra: slotsPurchased
} }
} }
}; };
@ -277,6 +283,7 @@ const handleBoosterPackPurchase = async (
BoosterPackItems: "", BoosterPackItems: "",
InventoryChanges: {} InventoryChanges: {}
}; };
const inventory = await getInventory(accountId);
for (let i = 0; i != quantity; ++i) { for (let i = 0; i != quantity; ++i) {
for (const weights of pack.rarityWeightsPerRoll) { for (const weights of pack.rarityWeightsPerRoll) {
const result = getRandomWeightedReward(pack.components, weights); const result = getRandomWeightedReward(pack.components, weights);
@ -286,11 +293,12 @@ const handleBoosterPackPurchase = async (
result.type.split("/Lotus/").join("/Lotus/StoreItems/") + ',{"lvl":0};'; result.type.split("/Lotus/").join("/Lotus/StoreItems/") + ',{"lvl":0};';
combineInventoryChanges( combineInventoryChanges(
purchaseResponse.InventoryChanges, purchaseResponse.InventoryChanges,
(await addItem(accountId, result.type, result.itemCount)).InventoryChanges (await addItem(inventory, result.type, result.itemCount)).InventoryChanges
); );
} }
} }
} }
await inventory.save();
return purchaseResponse; return purchaseResponse;
}; };
@ -303,12 +311,16 @@ const handleTypesPurchase = async (
const typeCategory = getStoreItemTypesCategory(typesName); const typeCategory = getStoreItemTypesCategory(typesName);
logger.debug(`type category ${typeCategory}`); logger.debug(`type category ${typeCategory}`);
switch (typeCategory) { switch (typeCategory) {
default: default: {
return await addItem(accountId, typesName, quantity); const inventory = await getInventory(accountId);
const resp = await addItem(inventory, typesName, quantity);
await inventory.save();
return resp;
}
case "BoosterPacks": case "BoosterPacks":
return await handleBoosterPackPurchase(typesName, accountId, quantity); return await handleBoosterPackPurchase(typesName, accountId, quantity);
case "SlotItems": 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); const roomToPlaceIn = rooms.find(room => room.Name === placedDecoration.Room);
if (!roomToPlaceIn) { if (!roomToPlaceIn) {
logger.error("room not found");
throw new Error("room not found"); throw new Error("room not found");
} }
@ -59,7 +58,6 @@ export const handleSetShipDecorations = async (
); );
if (existingDecorationIndex === -1) { if (existingDecorationIndex === -1) {
logger.error("decoration to be moved not found");
throw new 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); const room = personalRooms.Ship.Rooms.find(room => room.Name === req.Room);
if (!room) { if (!room) {
logger.error("room not found");
throw new Error("room not found"); throw new Error("room not found");
} }
const placedDeco = room.PlacedDecos?.find(x => x._id.toString() == req.DecoId); const placedDeco = room.PlacedDecos?.find(x => x._id.toString() == req.DecoId);
if (!placedDeco) { if (!placedDeco) {
logger.error("deco not found");
throw new Error("deco not found"); throw new Error("deco not found");
} }

View File

@ -1,6 +1,5 @@
import { Ship } from "@/src/models/shipModel"; import { Ship } from "@/src/models/shipModel";
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes"; import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
import { logger } from "@/src/utils/logger";
import { Types } from "mongoose"; import { Types } from "mongoose";
export const createShip = async ( 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); const ship = await Ship.findOne({ _id: shipId }, fieldSelection);
if (!ship) { if (!ship) {
logger.error(`error finding a ship with id ${shipId.toString()}`);
throw new 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"); }>("LoadOutInventory.LoadOutPresets");
if (!ship) { if (!ship) {
logger.error(`error finding a ship for account ${shipOwnerId}`);
throw new 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; hide?: boolean;
} }
export interface IEquipmentClient extends Omit<IEquipmentDatabase, "_id"> { export interface IEquipmentClient extends Omit<IEquipmentDatabase, "_id" | "UpgradesExpiry"> {
ItemId: IOid; ItemId: IOid;
UpgradesExpiry?: IMongoDate;
} }
export enum EquipmentFeatures { export enum EquipmentFeatures {
@ -112,6 +113,9 @@ export interface IEquipmentDatabase {
UnlockLevel?: number; UnlockLevel?: number;
Expiry?: IMongoDate; Expiry?: IMongoDate;
SkillTree?: string; SkillTree?: string;
OffensiveUpgrade?: string;
DefensiveUpgrade?: string;
UpgradesExpiry?: Date;
ArchonCrystalUpgrades?: IArchonCrystalUpgrade[]; ArchonCrystalUpgrades?: IArchonCrystalUpgrade[];
_id: Types.ObjectId; _id: Types.ObjectId;
} }

View File

@ -10,8 +10,6 @@ import {
IEquipmentDatabase IEquipmentDatabase
} from "@/src/types/inventoryTypes/commonInventoryTypes"; } 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 export interface IInventoryDatabase
extends Omit< extends Omit<
IInventoryResponse, IInventoryResponse,
@ -515,13 +513,15 @@ export interface IFusionTreasure {
Sockets: number; Sockets: number;
} }
export interface IHelminthFoodRecord {
ItemType: string;
Date: number;
}
export interface IHelminthResource { export interface IHelminthResource {
ItemType: string; ItemType: string;
Count: number; Count: number;
RecentlyConvertedResources?: { RecentlyConvertedResources?: IHelminthFoodRecord[];
ItemType: string;
Date: number;
}[];
} }
export interface IInfestedFoundry { export interface IInfestedFoundry {

View File

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

View File

@ -4346,5 +4346,17 @@
{ {
"scans": 9999, "scans": 9999,
"type": "/Lotus/Weapons/Infested/Melee/InfBoomerang/InfBoomerangSpawnAvatar" "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"> <div class="card mb-4">
<h5 class="card-header">Account</h5> <h5 class="card-header">Account</h5>
<div class="card-body"> <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> </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 // Powersuit Route
single.getRoute("#powersuit-route").on("beforeload", function () { single.getRoute("#powersuit-route").on("beforeload", function () {