diff --git a/src/controllers/api/arcaneCommonController.ts b/src/controllers/api/arcaneCommonController.ts index 2caccc29..1028fc3f 100644 --- a/src/controllers/api/arcaneCommonController.ts +++ b/src/controllers/api/arcaneCommonController.ts @@ -44,7 +44,7 @@ export const arcaneCommonController: RequestHandler = async (req, res) => { ItemType: json.arcane.ItemType, UpgradeFingerprint: JSON.stringify({ lvl: json.newRank }) }); - upgradeId = inventory.Upgrades[newLength - 1]._id!.toString(); + upgradeId = inventory.Upgrades[newLength - 1]._id.toString(); } // Remove RawUpgrades diff --git a/src/controllers/api/artifactsController.ts b/src/controllers/api/artifactsController.ts index a5ec66fa..564d222a 100644 --- a/src/controllers/api/artifactsController.ts +++ b/src/controllers/api/artifactsController.ts @@ -1,7 +1,7 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { RequestHandler } from "express"; -import { ICrewShipSalvagedWeaponSkin } from "@/src/types/inventoryTypes/inventoryTypes"; +import { IInventoryClient, IUpgradeClient } from "@/src/types/inventoryTypes/inventoryTypes"; import { addMods, getInventory } from "@/src/services/inventoryService"; import { config } from "@/src/services/configService"; @@ -20,7 +20,7 @@ export const artifactsController: RequestHandler = async (req, res) => { parsedUpgradeFingerprint.lvl += LevelDiff; const stringifiedUpgradeFingerprint = JSON.stringify(parsedUpgradeFingerprint); - let itemIndex = Upgrades.findIndex(upgrade => upgrade._id?.equals(ItemId!.$oid)); + let itemIndex = Upgrades.findIndex(upgrade => upgrade._id.equals(ItemId.$oid)); if (itemIndex !== -1) { Upgrades[itemIndex].UpgradeFingerprint = stringifiedUpgradeFingerprint; @@ -58,7 +58,7 @@ export const artifactsController: RequestHandler = async (req, res) => { } const changedInventory = await inventory.save(); - const itemId = changedInventory.toJSON().Upgrades[itemIndex]?.ItemId?.$oid; + const itemId = changedInventory.toJSON().Upgrades[itemIndex].ItemId.$oid; if (!itemId) { throw new Error("Item Id not found in upgradeMod"); @@ -68,7 +68,7 @@ export const artifactsController: RequestHandler = async (req, res) => { }; interface IArtifactsRequest { - Upgrade: ICrewShipSalvagedWeaponSkin; + Upgrade: IUpgradeClient; LevelDiff: number; Cost: number; FusionPointCost: number; diff --git a/src/controllers/api/getShipController.ts b/src/controllers/api/getShipController.ts index 3ce7a6c3..10f6e25f 100644 --- a/src/controllers/api/getShipController.ts +++ b/src/controllers/api/getShipController.ts @@ -4,10 +4,10 @@ import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { getPersonalRooms } from "@/src/services/personalRoomsService"; import { getShip } from "@/src/services/shipService"; -import { Loadout } from "@/src/models/inventoryModels/loadoutModel"; import { toOid } from "@/src/helpers/inventoryHelpers"; import { IGetShipResponse } from "@/src/types/shipTypes"; import { IPersonalRooms } from "@/src/types/personalRoomsTypes"; +import { getLoadout } from "@/src/services/loadoutService"; export const getShipController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -38,13 +38,3 @@ export const getShipController: RequestHandler = async (req, res) => { res.json(getShipResponse); }; - -export const getLoadout = async (accountId: string) => { - const loadout = await Loadout.findOne({ loadoutOwnerId: accountId }); - - if (!loadout) { - throw new Error(`loadout not found for account ${accountId}`); - } - - return loadout; -}; diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index f7567b01..166cafc1 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -6,7 +6,7 @@ import { IOid } from "@/src/types/commonTypes"; import { IConsumedSuit, IHelminthFoodRecord, - IInfestedFoundry, + IInfestedFoundryDatabase, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; @@ -356,7 +356,7 @@ interface IHelminthFeedRequest { }[]; } -export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number): ITypeCount[] => { +export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundryDatabase, delta: number): ITypeCount[] => { const recipeChanges: ITypeCount[] = []; infestedFoundry.XP ??= 0; const prevXP = infestedFoundry.XP; diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts index 5b143728..b4e0035d 100644 --- a/src/controllers/api/inventoryController.ts +++ b/src/controllers/api/inventoryController.ts @@ -4,7 +4,7 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel"; import { config } from "@/src/services/configService"; import allDialogue from "@/static/fixed_responses/allDialogue.json"; import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes"; -import { IInventoryResponse, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes"; +import { IInventoryClient, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes"; import { IPolarity, ArtifactPolarity, EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { ExportCustoms, @@ -55,7 +55,7 @@ export const inventoryController: RequestHandler = async (request, response) => const inventoryWithLoadOutPresetsAndShips = await inventoryWithLoadOutPresets.populate<{ Ships: IShipInventory }>( "Ships" ); - const inventoryResponse = inventoryWithLoadOutPresetsAndShips.toJSON(); + const inventoryResponse = inventoryWithLoadOutPresetsAndShips.toJSON(); if (config.infiniteCredits) { inventoryResponse.RegularCredits = 999999999; @@ -147,8 +147,9 @@ export const inventoryController: RequestHandler = async (request, response) => } if (config.unlockAllSkins) { - inventoryResponse.WeaponSkins = []; - for (const uniqueName in ExportCustoms) { + const missingWeaponSkins = new Set(Object.keys(ExportCustoms)); + inventoryResponse.WeaponSkins.forEach(x => missingWeaponSkins.delete(x.ItemType)); + for (const uniqueName of missingWeaponSkins) { inventoryResponse.WeaponSkins.push({ ItemId: { $oid: "ca70ca70ca70ca70" + catBreadHash(uniqueName).toString(16).padStart(8, "0") diff --git a/src/controllers/api/upgradesController.ts b/src/controllers/api/upgradesController.ts index bbfd2992..d295a462 100644 --- a/src/controllers/api/upgradesController.ts +++ b/src/controllers/api/upgradesController.ts @@ -6,7 +6,7 @@ import { EquipmentFeatures, IAbilityOverride } from "@/src/types/inventoryTypes/commonInventoryTypes"; -import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; +import { IInventoryClient, IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { getRecipeByResult } from "@/src/services/itemDataService"; @@ -62,7 +62,7 @@ export const upgradesController: RequestHandler = async (req, res) => { addRecipes(inventory, recipeChanges); inventoryChanges.Recipes = recipeChanges; - inventoryChanges.InfestedFoundry = inventory.toJSON().InfestedFoundry; + inventoryChanges.InfestedFoundry = inventory.toJSON().InfestedFoundry; } else switch (operation.UpgradeRequirement) { case "/Lotus/Types/Items/MiscItems/OrokinReactor": diff --git a/src/controllers/custom/getItemListsController.ts b/src/controllers/custom/getItemListsController.ts index 5033d492..04f2c746 100644 --- a/src/controllers/custom/getItemListsController.ts +++ b/src/controllers/custom/getItemListsController.ts @@ -2,6 +2,7 @@ import { RequestHandler } from "express"; import { getDict, getItemName, getString } from "@/src/services/itemDataService"; import { ExportArcanes, + ExportAvionics, ExportGear, ExportRecipes, ExportResources, @@ -137,6 +138,13 @@ const getItemListsController: RequestHandler = (req, response) => { badItems[uniqueName] = true; } } + for (const [uniqueName, upgrade] of Object.entries(ExportAvionics)) { + res.mods.push({ + uniqueName, + name: getString(upgrade.name, lang), + fusionLimit: upgrade.fusionLimit + }); + } for (const [uniqueName, arcane] of Object.entries(ExportArcanes)) { res.mods.push({ uniqueName, diff --git a/src/controllers/custom/importController.ts b/src/controllers/custom/importController.ts new file mode 100644 index 00000000..77d4d2cb --- /dev/null +++ b/src/controllers/custom/importController.ts @@ -0,0 +1,27 @@ +import { importInventory, importLoadOutPresets } from "@/src/services/importService"; +import { getInventory } from "@/src/services/inventoryService"; +import { getLoadout } from "@/src/services/loadoutService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { IInventoryClient } from "@/src/types/inventoryTypes/inventoryTypes"; +import { RequestHandler } from "express"; + +export const importController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const request = JSON.parse(String(req.body)) as IImportRequest; + + const inventory = await getInventory(accountId); + importInventory(inventory, request.inventory); + await inventory.save(); + + if (request.inventory.LoadOutPresets) { + const loadout = await getLoadout(accountId); + importLoadOutPresets(loadout, request.inventory.LoadOutPresets); + await loadout.save(); + } + + res.end(); +}; + +interface IImportRequest { + inventory: Partial; +} diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index d3b6b33b..ef7401ed 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -2,11 +2,10 @@ import { Document, Model, Schema, Types, model } from "mongoose"; import { IFlavourItem, IRawUpgrade, - ICrewShipSalvagedWeaponSkin, IMiscItem, IInventoryDatabase, IBooster, - IInventoryResponse, + IInventoryClient, ISlots, IMailbox, IDuviriInfo, @@ -14,7 +13,7 @@ import { IPendingRecipeResponse, ITypeCount, IFocusXP, - IFocusUpgrades, + IFocusUpgrade, ITypeXPItem, IChallengeProgress, IStepSequencer, @@ -24,7 +23,7 @@ import { ISeasonChallenge, IPlayerSkills, ISettings, - IInfestedFoundry, + IInfestedFoundryDatabase, IHelminthResource, IConsumedSuit, IQuestProgress, @@ -43,7 +42,6 @@ import { ICrewShipCustomization, ICrewShipWeapon, ICrewShipMembersClient, - ICrewShip, ICrewShipPilotWeapon, IShipExterior, IHelminthFoodRecord, @@ -52,7 +50,9 @@ import { IDialogueDatabase, IDialogueGift, ICompletedDialogue, - IDialogueClient + IDialogueClient, + IUpgradeDatabase, + ICrewShipDatabase } from "../../types/inventoryTypes/inventoryTypes"; import { IOid } from "../../types/commonTypes"; import { @@ -62,7 +62,6 @@ import { IOperatorConfigDatabase, IPolarity, IEquipmentDatabase, - IOperatorConfigClient, IArchonCrystalUpgrade } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers"; @@ -81,7 +80,7 @@ const focusXPSchema = new Schema( { _id: false } ); -const focusUpgradesSchema = new Schema( +const focusUpgradeSchema = new Schema( { ItemType: String, Level: Number, @@ -288,7 +287,7 @@ RawUpgrades.set("toJSON", { } }); -const upgradesSchema = new Schema( +const upgradeSchema = new Schema( { UpgradeFingerprint: String, PendingRerollFingerprint: { type: String, required: false }, @@ -297,11 +296,11 @@ const upgradesSchema = new Schema( { id: false } ); -upgradesSchema.virtual("ItemId").get(function () { +upgradeSchema.virtual("ItemId").get(function () { return toOid(this._id); }); -upgradesSchema.set("toJSON", { +upgradeSchema.set("toJSON", { virtuals: true, transform(_document, returnedObject) { delete returnedObject._id; @@ -493,7 +492,7 @@ const helminthResourceSchema = new Schema( { _id: false } ); -const infestedFoundrySchema = new Schema( +const infestedFoundrySchema = new Schema( { Name: String, Resources: { type: [helminthResourceSchema], default: undefined }, @@ -695,7 +694,7 @@ crewShipMembersSchema.set("toJSON", { } }); -const crewShipSchema = new Schema({ +const crewShipSchema = new Schema({ ItemType: { type: String, required: true }, Configs: { type: [ItemConfigSchema], default: [] }, Weapon: { type: crewShipWeaponSchema, default: undefined }, @@ -837,7 +836,7 @@ const inventorySchema = new Schema( //Curent active like Active school focuses is = "Zenurik" FocusAbility: String, //The treeways of the Focus school.(Active and passive Ability) - FocusUpgrades: [focusUpgradesSchema], + FocusUpgrades: [focusUpgradeSchema], //Achievement ChallengeProgress: [challengeProgressSchema], @@ -848,7 +847,7 @@ const inventorySchema = new Schema( //Non Upgrade Mods Example:I have 999 item WeaponElectricityDamageMod (only "ItemCount"+"ItemType") RawUpgrades: [RawUpgrades], //Upgrade Mods\Riven\Arcane Example:"UpgradeFingerprint"+"ItemType"+"" - Upgrades: [upgradesSchema], + Upgrades: [upgradeSchema], //Warframe Suits: [EquipmentSchema], @@ -1142,7 +1141,7 @@ inventorySchema.set("toJSON", { delete returnedObject.accountOwnerId; const inventoryDatabase = returnedObject as IInventoryDatabase; - const inventoryResponse = returnedObject as IInventoryResponse; + const inventoryResponse = returnedObject as IInventoryClient; inventoryResponse.TrainingDate = toMongoDate(inventoryDatabase.TrainingDate); inventoryResponse.Created = toMongoDate(inventoryDatabase.Created); @@ -1164,12 +1163,12 @@ type InventoryDocumentProps = { OperatorAmps: Types.DocumentArray; FlavourItems: Types.DocumentArray; RawUpgrades: Types.DocumentArray; - Upgrades: Types.DocumentArray; + Upgrades: Types.DocumentArray; MiscItems: Types.DocumentArray; Boosters: Types.DocumentArray; - OperatorLoadOuts: Types.DocumentArray; + OperatorLoadOuts: Types.DocumentArray; SpecialItems: Types.DocumentArray; - AdultOperatorLoadOuts: Types.DocumentArray; //TODO: this should still contain _id + AdultOperatorLoadOuts: Types.DocumentArray; MechSuits: Types.DocumentArray; Scoops: Types.DocumentArray; DataKnives: Types.DocumentArray; @@ -1185,7 +1184,7 @@ type InventoryDocumentProps = { Hoverboards: Types.DocumentArray; MoaPets: Types.DocumentArray; WeaponSkins: Types.DocumentArray; - CrewShips: Types.DocumentArray; + CrewShips: Types.DocumentArray; CrewShipHarnesses: Types.DocumentArray; }; diff --git a/src/routes/custom.ts b/src/routes/custom.ts index e4299412..3772362c 100644 --- a/src/routes/custom.ts +++ b/src/routes/custom.ts @@ -9,6 +9,7 @@ import { renameAccountController } from "@/src/controllers/custom/renameAccountC import { createAccountController } from "@/src/controllers/custom/createAccountController"; import { addItemsController } from "@/src/controllers/custom/addItemsController"; +import { importController } from "@/src/controllers/custom/importController"; import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController"; import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController"; @@ -24,6 +25,7 @@ customRouter.get("/renameAccount", renameAccountController); customRouter.post("/createAccount", createAccountController); customRouter.post("/addItems", addItemsController); +customRouter.post("/import", importController); customRouter.get("/config", getConfigDataController); customRouter.post("/config", updateConfigDataController); diff --git a/src/routes/webui.ts b/src/routes/webui.ts index 64b3d613..5ae72040 100644 --- a/src/routes/webui.ts +++ b/src/routes/webui.ts @@ -34,6 +34,9 @@ webuiRouter.get("/webui/settings", (_req, res) => { webuiRouter.get("/webui/cheats", (_req, res) => { res.sendFile(path.join(rootDir, "static/webui/index.html")); }); +webuiRouter.get("/webui/import", (_req, res) => { + res.sendFile(path.join(rootDir, "static/webui/index.html")); +}); // Serve static files webuiRouter.use("/webui", express.static(path.join(rootDir, "static/webui"))); diff --git a/src/services/importService.ts b/src/services/importService.ts new file mode 100644 index 00000000..91857653 --- /dev/null +++ b/src/services/importService.ts @@ -0,0 +1,247 @@ +import { Types } from "mongoose"; +import { + IEquipmentClient, + IEquipmentDatabase, + IOperatorConfigClient, + IOperatorConfigDatabase +} from "../types/inventoryTypes/commonInventoryTypes"; +import { IMongoDate } from "../types/commonTypes"; +import { + equipmentKeys, + ICrewShipClient, + ICrewShipDatabase, + ICrewShipMembersClient, + ICrewShipMembersDatabase, + IDialogueClient, + IDialogueDatabase, + IDialogueHistoryClient, + IDialogueHistoryDatabase, + IInfestedFoundryClient, + IInfestedFoundryDatabase, + IInventoryClient, + ILoadoutConfigClient, + ILoadOutPresets, + ISlots, + IUpgradeClient, + IUpgradeDatabase, + IWeaponSkinClient, + IWeaponSkinDatabase +} from "../types/inventoryTypes/inventoryTypes"; +import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel"; +import { ILoadoutConfigDatabase, ILoadoutDatabase } from "../types/saveLoadoutTypes"; + +const convertDate = (value: IMongoDate): Date => { + return new Date(parseInt(value.$date.$numberLong)); +}; + +const convertOptionalDate = (value: IMongoDate | undefined): Date | undefined => { + return value ? convertDate(value) : undefined; +}; + +const convertEquipment = (client: IEquipmentClient): IEquipmentDatabase => { + const { ItemId, ...rest } = client; + return { + ...rest, + _id: new Types.ObjectId(ItemId.$oid), + InfestationDate: convertOptionalDate(client.InfestationDate), + Expiry: convertOptionalDate(client.Expiry), + UpgradesExpiry: convertOptionalDate(client.UpgradesExpiry) + }; +}; + +const convertWeaponSkin = (client: IWeaponSkinClient): IWeaponSkinDatabase => { + const { ItemId, ...rest } = client; + return { + ...rest, + _id: new Types.ObjectId(ItemId.$oid) + }; +}; + +const convertUpgrade = (client: IUpgradeClient): IUpgradeDatabase => { + const { ItemId, ...rest } = client; + return { + ...rest, + _id: new Types.ObjectId(ItemId.$oid) + }; +}; + +const convertOperatorConfig = (client: IOperatorConfigClient): IOperatorConfigDatabase => { + const { ItemId, ...rest } = client; + return { + ...rest, + _id: new Types.ObjectId(ItemId.$oid) + }; +}; + +const replaceArray = (arr: T[], replacement: T[]): void => { + arr.splice(0, arr.length); + replacement.forEach(x => { + arr.push(x); + }); +}; + +const replaceSlots = (db: ISlots, client: ISlots): void => { + db.Extra = client.Extra; + db.Slots = client.Slots; +}; + +const convertCrewShipMembers = (client: ICrewShipMembersClient): ICrewShipMembersDatabase => { + return { + SLOT_A: client.SLOT_A ? new Types.ObjectId(client.SLOT_A.ItemId.$oid) : undefined, + SLOT_B: client.SLOT_B ? new Types.ObjectId(client.SLOT_B.ItemId.$oid) : undefined, + SLOT_C: client.SLOT_C ? new Types.ObjectId(client.SLOT_C.ItemId.$oid) : undefined + }; +}; + +const convertCrewShip = (client: ICrewShipClient): ICrewShipDatabase => { + const { ItemId, ...rest } = client; + return { + ...rest, + _id: new Types.ObjectId(ItemId.$oid), + CrewMembers: client.CrewMembers ? convertCrewShipMembers(client.CrewMembers) : undefined + }; +}; + +const convertInfestedFoundry = (client: IInfestedFoundryClient): IInfestedFoundryDatabase => { + return { + ...client, + LastConsumedSuit: client.LastConsumedSuit ? convertEquipment(client.LastConsumedSuit) : undefined, + AbilityOverrideUnlockCooldown: convertOptionalDate(client.AbilityOverrideUnlockCooldown) + }; +}; + +const convertDialogue = (client: IDialogueClient): IDialogueDatabase => { + return { + ...client, + AvailableDate: convertDate(client.AvailableDate), + AvailableGiftDate: convertDate(client.AvailableGiftDate), + RankUpExpiry: convertDate(client.RankUpExpiry), + BountyChemExpiry: convertDate(client.BountyChemExpiry) + }; +}; + +const convertDialogueHistory = (client: IDialogueHistoryClient): IDialogueHistoryDatabase => { + return { + YearIteration: client.YearIteration, + Dialogues: client.Dialogues ? client.Dialogues.map(convertDialogue) : undefined + }; +}; + +export const importInventory = (db: TInventoryDatabaseDocument, client: Partial): void => { + for (const key of equipmentKeys) { + if (client[key]) { + replaceArray(db[key], client[key].map(convertEquipment)); + } + } + if (client.WeaponSkins) { + replaceArray(db.WeaponSkins, client.WeaponSkins.map(convertWeaponSkin)); + } + if (client.Upgrades) { + replaceArray(db.Upgrades, client.Upgrades.map(convertUpgrade)); + } + for (const key of ["RawUpgrades", "MiscItems"] as const) { + if (client[key]) { + db[key].splice(0, db[key].length); + client[key].forEach(x => { + db[key].push({ + ItemType: x.ItemType, + ItemCount: x.ItemCount + }); + }); + } + } + for (const key of ["OperatorLoadOuts", "AdultOperatorLoadOuts"] as const) { + if (client[key]) { + replaceArray(db[key], client[key].map(convertOperatorConfig)); + } + } + for (const key of [ + "SuitBin", + "WeaponBin", + "SentinelBin", + "SpaceSuitBin", + "SpaceWeaponBin", + "PvpBonusLoadoutBin", + "PveBonusLoadoutBin", + "RandomModBin", + "MechBin", + "CrewMemberBin", + "OperatorAmpBin", + "CrewShipSalvageBin" + ] as const) { + if (client[key]) { + replaceSlots(db[key], client[key]); + } + } + if (client.UseAdultOperatorLoadout) { + db.UseAdultOperatorLoadout = client.UseAdultOperatorLoadout; + } + for (const key of [ + "PlayerLevel", + "RegularCredits", + "PremiumCredits", + "PremiumCreditsFree", + "FusionPoints", + "PrimeTokens" + ] as const) { + if (client[key]) { + db[key] = client[key]; + } + } + for (const key of ["ThemeStyle", "ThemeBackground", "ThemeSounds", "EquippedInstrument", "FocusAbility"] as const) { + if (client[key]) { + db[key] = client[key]; + } + } + for (const key of ["EquippedGear", "EquippedEmotes", "NodeIntrosCompleted"] as const) { + if (client[key]) { + db[key] = client[key]; + } + } + if (client.XPInfo) { + db.XPInfo = client.XPInfo; + } + if (client.CurrentLoadOutIds) { + db.CurrentLoadOutIds = client.CurrentLoadOutIds; + } + if (client.Affiliations) { + db.Affiliations = client.Affiliations; + } + if (client.FusionTreasures) { + db.FusionTreasures = client.FusionTreasures; + } + if (client.FocusUpgrades) { + db.FocusUpgrades = client.FocusUpgrades; + } + if (client.CrewShips) { + replaceArray(db.CrewShips, client.CrewShips.map(convertCrewShip)); + } + if (client.InfestedFoundry) { + db.InfestedFoundry = convertInfestedFoundry(client.InfestedFoundry); + } + if (client.DialogueHistory) { + db.DialogueHistory = convertDialogueHistory(client.DialogueHistory); + } +}; + +const convertLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => { + const { ItemId, ...rest } = client; + return { + ...rest, + _id: new Types.ObjectId(ItemId.$oid) + }; +}; + +export const importLoadOutPresets = (db: ILoadoutDatabase, client: ILoadOutPresets): void => { + db.NORMAL = client.NORMAL.map(convertLoadOutConfig); + db.SENTINEL = client.SENTINEL.map(convertLoadOutConfig); + db.ARCHWING = client.ARCHWING.map(convertLoadOutConfig); + db.NORMAL_PVP = client.NORMAL_PVP.map(convertLoadOutConfig); + db.LUNARO = client.LUNARO.map(convertLoadOutConfig); + db.OPERATOR = client.OPERATOR.map(convertLoadOutConfig); + db.KDRIVE = client.KDRIVE.map(convertLoadOutConfig); + db.DATAKNIFE = client.DATAKNIFE.map(convertLoadOutConfig); + db.MECH = client.MECH.map(convertLoadOutConfig); + db.OPERATOR_ADULT = client.OPERATOR_ADULT.map(convertLoadOutConfig); + db.DRIFTER = client.DRIFTER.map(convertLoadOutConfig); +}; diff --git a/src/types/inventoryTypes/commonInventoryTypes.ts b/src/types/inventoryTypes/commonInventoryTypes.ts index ccc5851e..e1603816 100644 --- a/src/types/inventoryTypes/commonInventoryTypes.ts +++ b/src/types/inventoryTypes/commonInventoryTypes.ts @@ -78,8 +78,11 @@ export interface IEquipmentSelection { hide?: boolean; } -export interface IEquipmentClient extends Omit { +export interface IEquipmentClient + extends Omit { ItemId: IOid; + InfestationDate?: IMongoDate; + Expiry?: IMongoDate; UpgradesExpiry?: IMongoDate; } @@ -106,12 +109,12 @@ export interface IEquipmentDatabase { CustomizationSlotPurchases?: number; UpgradeType?: string; UpgradeFingerprint?: string; - InfestationDate?: IMongoDate; + InfestationDate?: Date; InfestationDays?: number; InfestationType?: string; ModularParts?: string[]; UnlockLevel?: number; - Expiry?: IMongoDate; + Expiry?: Date; SkillTree?: string; OffensiveUpgrade?: string; DefensiveUpgrade?: string; diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 3710ca2b..d5af0066 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -7,12 +7,14 @@ import { IItemConfig, IOperatorConfigClient, IEquipmentSelection, - IEquipmentDatabase + IEquipmentDatabase, + IEquipmentClient, + IOperatorConfigDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes"; export interface IInventoryDatabase extends Omit< - IInventoryResponse, + IInventoryClient, | "TrainingDate" | "LoadOutPresets" | "Mailbox" @@ -23,6 +25,15 @@ export interface IInventoryDatabase | "BlessingCooldown" | "Ships" | "WeaponSkins" + | "Upgrades" + | "CrewShipSalvagedWeaponSkins" + | "CrewShipWeaponSkins" + | "OperatorLoadOuts" + | "AdultOperatorLoadOuts" + | "CrewShips" + | "InfestedFoundry" + | "DialogueHistory" + | TEquipmentKey > { accountOwnerId: Types.ObjectId; Created: Date; @@ -35,6 +46,28 @@ export interface IInventoryDatabase BlessingCooldown: Date; Ships: Types.ObjectId[]; WeaponSkins: IWeaponSkinDatabase[]; + Upgrades: IUpgradeDatabase[]; + CrewShipSalvagedWeaponSkins: IUpgradeDatabase[]; + CrewShipWeaponSkins: IUpgradeDatabase[]; + OperatorLoadOuts: IOperatorConfigDatabase[]; + AdultOperatorLoadOuts: IOperatorConfigDatabase[]; + CrewShips: ICrewShipDatabase[]; + InfestedFoundry?: IInfestedFoundryDatabase; + DialogueHistory?: IDialogueHistoryDatabase; + + Suits: IEquipmentDatabase[]; + LongGuns: IEquipmentDatabase[]; + Pistols: IEquipmentDatabase[]; + Melee: IEquipmentDatabase[]; + SpecialItems: IEquipmentDatabase[]; + Sentinels: IEquipmentDatabase[]; + SentinelWeapons: IEquipmentDatabase[]; + SpaceSuits: IEquipmentDatabase[]; + SpaceGuns: IEquipmentDatabase[]; + SpaceMelee: IEquipmentDatabase[]; + Hoverboards: IEquipmentDatabase[]; + OperatorAmps: IEquipmentDatabase[]; + MoaPets: IEquipmentDatabase[]; } export interface IQuestKeyDatabase { @@ -46,12 +79,6 @@ export interface IQuestKeyDatabase { CompletionDate?: Date; } -export interface IFocusUpgrades { - ItemType: string; - Level: number; - IsUniversal: boolean; -} - export interface ITypeCount { ItemType: string; ItemCount: number; @@ -126,7 +153,21 @@ export interface IDailyAffiliations { DailyAffiliationHex: number; } -export interface IInventoryResponse extends IDailyAffiliations { +export interface IInventoryClient extends IDailyAffiliations { + Suits: IEquipmentClient[]; + LongGuns: IEquipmentClient[]; + Pistols: IEquipmentClient[]; + Melee: IEquipmentClient[]; + SpecialItems: IEquipmentClient[]; + Sentinels: IEquipmentClient[]; + SentinelWeapons: IEquipmentClient[]; + SpaceSuits: IEquipmentClient[]; + SpaceGuns: IEquipmentClient[]; + SpaceMelee: IEquipmentClient[]; + Hoverboards: IEquipmentClient[]; + OperatorAmps: IEquipmentClient[]; + MoaPets: IEquipmentClient[]; + Horses: IEquipmentDatabase[]; DrifterMelee: IEquipmentDatabase[]; DrifterGuns: IEquipmentDatabase[]; @@ -163,17 +204,13 @@ export interface IInventoryResponse extends IDailyAffiliations { ChallengeProgress: IChallengeProgress[]; RawUpgrades: IRawUpgrade[]; ReceivedStartingGear: boolean; - Suits: IEquipmentDatabase[]; - LongGuns: IEquipmentDatabase[]; - Pistols: IEquipmentDatabase[]; - Melee: IEquipmentDatabase[]; Ships: IShipInventory[]; QuestKeys: IQuestKeyResponse[]; FlavourItems: IFlavourItem[]; Scoops: IEquipmentDatabase[]; TrainingRetriesLeft: number; LoadOutPresets: ILoadOutPresets; - CurrentLoadOutIds: Array; + CurrentLoadOutIds: IOid[]; // we store it in the database using this representation as well :/ Missions: IMission[]; RandomUpgradesIdentified?: number; LastRegionPlayed: TSolarMapRegion; @@ -191,7 +228,7 @@ export interface IInventoryResponse extends IDailyAffiliations { Accolades?: { Heirloom?: boolean; }; - Upgrades: ICrewShipSalvagedWeaponSkin[]; + Upgrades: IUpgradeClient[]; EquippedGear: string[]; DeathMarks: string[]; FusionTreasures: IFusionTreasure[]; @@ -213,14 +250,9 @@ export interface IInventoryResponse extends IDailyAffiliations { Affiliations: IAffiliation[]; QualifyingInvasions: any[]; FactionScores: number[]; - SpaceSuits: IEquipmentDatabase[]; - SpaceMelee: IEquipmentDatabase[]; - SpaceGuns: IEquipmentDatabase[]; ArchwingEnabled: boolean; PendingSpectreLoadouts?: ISpectreLoadout[]; SpectreLoadouts?: ISpectreLoadout[]; - SentinelWeapons: IEquipmentDatabase[]; - Sentinels: IEquipmentDatabase[]; EmailItems: ITypeCount[]; CompletedSyndicates: string[]; FocusXP: IFocusXP; @@ -237,13 +269,11 @@ export interface IInventoryResponse extends IDailyAffiliations { CompletedJobs: ICompletedJob[]; FocusAbility: string; FocusUpgrades: IFocusUpgrade[]; - OperatorAmps: IEquipmentDatabase[]; HasContributedToDojo?: boolean; HWIDProtectEnabled?: boolean; KubrowPetPrints: IKubrowPetPrint[]; AlignmentReplay: IAlignment; PersonalGoalProgress: IPersonalGoalProgress[]; - SpecialItems: IEquipmentDatabase[]; ThemeStyle: string; ThemeBackground: string; ThemeSounds: string; @@ -252,12 +282,10 @@ export interface IInventoryResponse extends IDailyAffiliations { LoginMilestoneRewards: string[]; OperatorLoadOuts: IOperatorConfigClient[]; RecentVendorPurchases: Array; - Hoverboards: IEquipmentDatabase[]; NodeIntrosCompleted: string[]; GuildId?: IOid; CompletedJobChains: ICompletedJobChain[]; SeasonChallengeHistory: ISeasonChallenge[]; - MoaPets: IEquipmentDatabase[]; EquippedInstrument?: string; InvasionChainProgress: IInvasionChainProgress[]; DataKnives: IEquipmentDatabase[]; @@ -266,18 +294,18 @@ export interface IInventoryResponse extends IDailyAffiliations { LastNemesisAllySpawnTime?: IMongoDate; Settings: ISettings; PersonalTechProjects: IPersonalTechProject[]; - CrewShips: ICrewShip[]; + CrewShips: ICrewShipClient[]; PlayerSkills: IPlayerSkills; CrewShipAmmo: IConsumable[]; - CrewShipSalvagedWeaponSkins: ICrewShipSalvagedWeaponSkin[]; + CrewShipSalvagedWeaponSkins: IUpgradeClient[]; CrewShipWeapons: ICrewShipWeapon[]; CrewShipSalvagedWeapons: ICrewShipWeapon[]; - CrewShipWeaponSkins: ICrewShipSalvagedWeaponSkin[]; + CrewShipWeaponSkins: IUpgradeClient[]; TradeBannedUntil?: IMongoDate; PlayedParkourTutorial: boolean; SubscribedToEmailsPersonalized: number; MechSuits: IEquipmentDatabase[]; - InfestedFoundry?: IInfestedFoundry; + InfestedFoundry?: IInfestedFoundryClient; BlessingCooldown: IMongoDate; CrewShipHarnesses: IEquipmentDatabase[]; CrewShipRawSalvage: IConsumable[]; @@ -304,7 +332,7 @@ export interface IInventoryResponse extends IDailyAffiliations { Harvestable: boolean; DeathSquadable: boolean; EndlessXP?: IEndlessXpProgress[]; - DialogueHistory?: IDialogueHistoryDatabase; + DialogueHistory?: IDialogueHistoryClient; } export interface IAffiliation { @@ -415,15 +443,18 @@ export interface ISlots { Slots: number; } -export interface ICrewShipSalvagedWeaponSkin { +export interface IUpgradeClient { ItemType: string; UpgradeFingerprint?: string; PendingRerollFingerprint?: string; - ItemId?: IOid; - _id?: Types.ObjectId; + ItemId: IOid; } -export interface ICrewShip { +export interface IUpgradeDatabase extends Omit { + _id: Types.ObjectId; +} + +export interface ICrewShipClient { ItemType: string; Configs: IItemConfig[]; Weapon?: ICrewShipWeapon; @@ -432,6 +463,10 @@ export interface ICrewShip { RailjackImage?: IFlavourItem; CrewMembers?: ICrewShipMembersClient; ItemId: IOid; +} + +export interface ICrewShipDatabase extends Omit { + CrewMembers?: ICrewShipMembersDatabase; _id: Types.ObjectId; } @@ -535,7 +570,7 @@ export interface IHelminthResource { RecentlyConvertedResources?: IHelminthFoodRecord[]; } -export interface IInfestedFoundry { +export interface IInfestedFoundryClient { Name?: string; Resources?: IHelminthResource[]; Slots?: number; @@ -544,6 +579,12 @@ export interface IInfestedFoundry { InvigorationIndex?: number; InvigorationSuitOfferings?: string[]; InvigorationsApplied?: number; + LastConsumedSuit?: IEquipmentClient; + AbilityOverrideUnlockCooldown?: IMongoDate; +} + +export interface IInfestedFoundryDatabase + extends Omit { LastConsumedSuit?: IEquipmentDatabase; AbilityOverrideUnlockCooldown?: Date; } @@ -636,7 +677,7 @@ export interface ILibraryPersonalProgress { Completed: boolean; } -//this needs to be checked against ILoadoutDatabase +// keep in sync with ILoadoutDatabase export interface ILoadOutPresets { NORMAL: ILoadoutConfigClient[]; NORMAL_PVP: ILoadoutConfigClient[]; @@ -649,6 +690,7 @@ export interface ILoadOutPresets { DATAKNIFE: ILoadoutConfigClient[]; MECH: ILoadoutConfigClient[]; OPERATOR_ADULT: ILoadoutConfigClient[]; + DRIFTER: ILoadoutConfigClient[]; } export enum FocusSchool { @@ -913,9 +955,10 @@ export interface ITaunt { export interface IWeaponSkinDatabase { ItemType: string; + _id: Types.ObjectId; } -export interface IWeaponSkinClient extends IWeaponSkinDatabase { +export interface IWeaponSkinClient extends Omit { ItemId: IOid; } diff --git a/src/types/purchaseTypes.ts b/src/types/purchaseTypes.ts index 2aca5212..e8f6d090 100644 --- a/src/types/purchaseTypes.ts +++ b/src/types/purchaseTypes.ts @@ -1,4 +1,4 @@ -import { IInfestedFoundry } from "./inventoryTypes/inventoryTypes"; +import { IInfestedFoundryClient } from "./inventoryTypes/inventoryTypes"; export interface IPurchaseRequest { PurchaseParams: IPurchaseParams; @@ -29,9 +29,9 @@ export interface ICurrencyChanges { export type IInventoryChanges = { [_ in SlotNames]?: IBinChanges; -} & ICurrencyChanges & { InfestedFoundry?: IInfestedFoundry } & Record< +} & ICurrencyChanges & { InfestedFoundry?: IInfestedFoundryClient } & Record< string, - IBinChanges | number | object[] | IInfestedFoundry + IBinChanges | number | object[] | IInfestedFoundryClient >; export interface IPurchaseResponse { diff --git a/src/types/saveLoadoutTypes.ts b/src/types/saveLoadoutTypes.ts index b7c5b1e1..cd32b5c1 100644 --- a/src/types/saveLoadoutTypes.ts +++ b/src/types/saveLoadoutTypes.ts @@ -54,18 +54,19 @@ export interface IConfigEntry { export interface ILoadoutClient extends Omit {} +// keep in sync with ILoadOutPresets export interface ILoadoutDatabase { - NORMAL: ILoadoutEntry; - SENTINEL: ILoadoutEntry; - ARCHWING: ILoadoutEntry; - NORMAL_PVP: ILoadoutEntry; - LUNARO: ILoadoutEntry; - OPERATOR: ILoadoutEntry; - KDRIVE: ILoadoutEntry; - DATAKNIFE: ILoadoutEntry; - MECH: ILoadoutEntry; - OPERATOR_ADULT: ILoadoutEntry; - DRIFTER: ILoadoutEntry; + NORMAL: ILoadoutConfigDatabase[]; + SENTINEL: ILoadoutConfigDatabase[]; + ARCHWING: ILoadoutConfigDatabase[]; + NORMAL_PVP: ILoadoutConfigDatabase[]; + LUNARO: ILoadoutConfigDatabase[]; + OPERATOR: ILoadoutConfigDatabase[]; + KDRIVE: ILoadoutConfigDatabase[]; + DATAKNIFE: ILoadoutConfigDatabase[]; + MECH: ILoadoutConfigDatabase[]; + OPERATOR_ADULT: ILoadoutConfigDatabase[]; + DRIFTER: ILoadoutConfigDatabase[]; _id: Types.ObjectId; loadoutOwnerId: Types.ObjectId; } diff --git a/static/webui/index.html b/static/webui/index.html index 6bbe59f4..6d19b3c9 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -64,6 +64,9 @@ + @@ -492,6 +495,11 @@ +
+

You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer will be overwritten in your account.

+ + +
diff --git a/static/webui/script.js b/static/webui/script.js index dc6e4872..9e0d970e 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -215,7 +215,12 @@ function updateInventory() { const td = document.createElement("td"); td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType; if (item.ItemName) { - td.textContent = item.ItemName + " (" + td.textContent + ")"; + const pipeIndex = item.ItemName.indexOf("|"); + if (pipeIndex != -1) { + td.textContent = item.ItemName.substr(1 + pipeIndex) + " " + td.textContent; + } else { + td.textContent = item.ItemName + " (" + td.textContent + ")"; + } } if (item.ModularParts && item.ModularParts.length) { td.textContent += " ["; @@ -1083,3 +1088,18 @@ function doPopArchonCrystalUpgrade(type) { }); }); } + +function doImport() { + revalidateAuthz(() => { + $.post({ + url: "/custom/import?" + window.authz, + contentType: "text/plain", + data: JSON.stringify({ + inventory: JSON.parse($("#import-inventory").val()) + }) + }).then(function () { + alert("Successfully imported."); + updateInventory(); + }); + }); +}