diff --git a/package-lock.json b/package-lock.json index 39d49f9c..37b14f4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -552,6 +552,7 @@ "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.41.0", "@typescript-eslint/types": "8.41.0", @@ -1180,6 +1181,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2057,6 +2059,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2223,6 +2226,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -4362,6 +4366,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -5203,6 +5208,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5421,6 +5427,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5480,6 +5487,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -5675,6 +5683,7 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", "license": "MIT", + "peer": true, "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", diff --git a/src/controllers/api/activateRandomModController.ts b/src/controllers/api/activateRandomModController.ts index ce452b37..ff9917b6 100644 --- a/src/controllers/api/activateRandomModController.ts +++ b/src/controllers/api/activateRandomModController.ts @@ -1,4 +1,4 @@ -import { toOid } from "../../helpers/inventoryHelpers.ts"; +import { toOid2 } from "../../helpers/inventoryHelpers.ts"; import { createVeiledRivenFingerprint, createUnveiledRivenFingerprint, @@ -6,13 +6,14 @@ import { } from "../../helpers/rivenHelper.ts"; import { getJSONfromString } from "../../helpers/stringHelpers.ts"; import { addMods, getInventory } from "../../services/inventoryService.ts"; -import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { getAccountForRequest } from "../../services/loginService.ts"; import { getRandomElement } from "../../services/rngService.ts"; import type { RequestHandler } from "express"; import { ExportUpgrades } from "warframe-public-export-plus"; export const activateRandomModController: RequestHandler = async (req, res) => { - const accountId = await getAccountIdForRequest(req); + const account = await getAccountForRequest(req); + const accountId = account._id.toString(); const inventory = await getInventory(accountId, "RawUpgrades Upgrades instantFinishRivenChallenge"); const request = getJSONfromString(String(req.body)); addMods(inventory, [ @@ -36,7 +37,7 @@ export const activateRandomModController: RequestHandler = async (req, res) => { NewMod: { UpgradeFingerprint: fingerprint, ItemType: rivenType, - ItemId: toOid(inventory.Upgrades[upgradeIndex]._id) + ItemId: toOid2(inventory.Upgrades[upgradeIndex]._id, account.BuildLabel) } }); }; diff --git a/src/controllers/api/artifactTransmutationController.ts b/src/controllers/api/artifactTransmutationController.ts index 69cc41ee..319f6441 100644 --- a/src/controllers/api/artifactTransmutationController.ts +++ b/src/controllers/api/artifactTransmutationController.ts @@ -1,7 +1,7 @@ -import { fromOid, toOid } from "../../helpers/inventoryHelpers.ts"; +import { fromOid, toOid2 } from "../../helpers/inventoryHelpers.ts"; import { createVeiledRivenFingerprint, rivenRawToRealWeighted } from "../../helpers/rivenHelper.ts"; import { addMiscItems, addMods, getInventory } from "../../services/inventoryService.ts"; -import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { getAccountForRequest } from "../../services/loginService.ts"; import { getRandomElement, getRandomWeightedReward, getRandomWeightedRewardUc } from "../../services/rngService.ts"; import type { IUpgradeFromClient } from "../../types/inventoryTypes/inventoryTypes.ts"; import type { RequestHandler } from "express"; @@ -9,12 +9,15 @@ import type { TRarity } from "warframe-public-export-plus"; import { ExportBoosterPacks, ExportUpgrades } from "warframe-public-export-plus"; export const artifactTransmutationController: RequestHandler = async (req, res) => { - const accountId = await getAccountIdForRequest(req); + const account = await getAccountForRequest(req); + const accountId = account._id.toString(); const inventory = await getInventory(accountId); const payload = JSON.parse(String(req.body)) as IArtifactTransmutationRequest; inventory.RegularCredits -= payload.Cost; - inventory.FusionPoints -= payload.FusionPointCost; + if (payload.FusionPointCost) { + inventory.FusionPoints -= payload.FusionPointCost; + } if (payload.RivenTransmute) { addMiscItems(inventory, [ @@ -41,7 +44,7 @@ export const artifactTransmutationController: RequestHandler = async (req, res) res.json({ NewMods: [ { - ItemId: toOid(inventory.Upgrades[upgradeIndex]._id), + ItemId: toOid2(inventory.Upgrades[upgradeIndex]._id, account.BuildLabel), ItemType: rivenType, UpgradeFingerprint: fingerprint } @@ -56,9 +59,10 @@ export const artifactTransmutationController: RequestHandler = async (req, res) }; let forcedPolarity: string | undefined; payload.Consumed.forEach(upgrade => { + upgrade.ItemCount ??= 1; const meta = ExportUpgrades[upgrade.ItemType]; counts[meta.rarity] += upgrade.ItemCount; - if (fromOid(upgrade.ItemId) != "000000000000000000000000") { + if (fromOid(upgrade.ItemId) != "" && fromOid(upgrade.ItemId) != "000000000000000000000000") { inventory.Upgrades.pull({ _id: fromOid(upgrade.ItemId) }); } else { addMods(inventory, [ @@ -133,7 +137,7 @@ interface IArtifactTransmutationRequest { LevelDiff: number; Consumed: IUpgradeFromClient[]; Cost: number; - FusionPointCost: number; + FusionPointCost?: number; RivenTransmute?: boolean; } diff --git a/src/controllers/api/artifactsController.ts b/src/controllers/api/artifactsController.ts index 362aa971..43555af4 100644 --- a/src/controllers/api/artifactsController.ts +++ b/src/controllers/api/artifactsController.ts @@ -1,63 +1,126 @@ import { getJSONfromString } from "../../helpers/stringHelpers.ts"; -import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { getAccountForRequest } from "../../services/loginService.ts"; import type { RequestHandler } from "express"; -import type { IInventoryClient, IUpgradeClient } from "../../types/inventoryTypes/inventoryTypes.ts"; +import type { + IInventoryClient, + IUpgradeClient, + IUpgradeFromClient +} from "../../types/inventoryTypes/inventoryTypes.ts"; import { addMods, getInventory } from "../../services/inventoryService.ts"; import { broadcastInventoryUpdate } from "../../services/wsService.ts"; +import { fromOid, version_compare } from "../../helpers/inventoryHelpers.ts"; export const artifactsController: RequestHandler = async (req, res) => { - const accountId = await getAccountIdForRequest(req); + const account = await getAccountForRequest(req); + const accountId = account._id.toString(); const artifactsData = getJSONfromString(String(req.body)); - const { Upgrade, LevelDiff, Cost, FusionPointCost } = artifactsData; + const { Upgrade, LevelDiff, Cost, FusionPointCost, Consumed, Fingerprint } = artifactsData; const inventory = await getInventory(accountId); const { Upgrades } = inventory; const { ItemType, UpgradeFingerprint, ItemId } = Upgrade; - const safeUpgradeFingerprint = UpgradeFingerprint || '{"lvl":0}'; - const parsedUpgradeFingerprint = JSON.parse(safeUpgradeFingerprint) as { lvl: number }; - parsedUpgradeFingerprint.lvl += LevelDiff; - const stringifiedUpgradeFingerprint = JSON.stringify(parsedUpgradeFingerprint); + if (!account.BuildLabel || version_compare(account.BuildLabel, "2016.08.19.17.12") >= 0) { + const safeUpgradeFingerprint = UpgradeFingerprint || '{"lvl":0}'; + const parsedUpgradeFingerprint = JSON.parse(safeUpgradeFingerprint) as { lvl: number }; + 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(fromOid(ItemId))); - if (itemIndex !== -1) { - Upgrades[itemIndex].UpgradeFingerprint = stringifiedUpgradeFingerprint; + if (itemIndex !== -1) { + Upgrades[itemIndex].UpgradeFingerprint = stringifiedUpgradeFingerprint; + } else { + itemIndex = + Upgrades.push({ + UpgradeFingerprint: stringifiedUpgradeFingerprint, + ItemType + }) - 1; + + addMods(inventory, [{ ItemType, ItemCount: -1 }]); + } + + if (!inventory.infiniteCredits) { + inventory.RegularCredits -= Cost; + } + if (!inventory.infiniteEndo) { + inventory.FusionPoints -= FusionPointCost; + } + + if (artifactsData.LegendaryFusion) { + addMods(inventory, [ + { + ItemType: "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser", + ItemCount: -1 + } + ]); + } + + const changedInventory = (await inventory.save()).toJSON(); + const itemId = + changedInventory.Upgrades[itemIndex].ItemId.$oid ?? changedInventory.Upgrades[itemIndex].ItemId.$id; + + if (!itemId) { + throw new Error("Item Id not found in upgradeMod"); + } + + res.send(itemId); } else { - itemIndex = - Upgrades.push({ - UpgradeFingerprint: stringifiedUpgradeFingerprint, - ItemType - }) - 1; + // Pre-U18.18.0 uses the old pre-Endo fusion system which uses a different UpgradeFingerprint format + // that has to be converted and consumes upgrades in the fusion proccess + const safeUpgradeFingerprint = `{"lvl":${Fingerprint?.substring(4, Fingerprint.lastIndexOf("|"))}}`; + const parsedUpgradeFingerprint = JSON.parse(safeUpgradeFingerprint) as { lvl: number }; + if (LevelDiff) { + parsedUpgradeFingerprint.lvl += LevelDiff; + } + const stringifiedUpgradeFingerprint = JSON.stringify(parsedUpgradeFingerprint); - addMods(inventory, [{ ItemType, ItemCount: -1 }]); - } + let itemIndex = Upgrades.findIndex(upgrade => upgrade._id.equals(ItemId.$id)); - if (!inventory.infiniteCredits) { - inventory.RegularCredits -= Cost; - } - if (!inventory.infiniteEndo) { - inventory.FusionPoints -= FusionPointCost; - } + if (itemIndex !== -1) { + Upgrades[itemIndex].UpgradeFingerprint = stringifiedUpgradeFingerprint; + } else { + itemIndex = + Upgrades.push({ + UpgradeFingerprint: stringifiedUpgradeFingerprint, + ItemType + }) - 1; - if (artifactsData.LegendaryFusion) { - addMods(inventory, [ - { - ItemType: "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser", - ItemCount: -1 + addMods(inventory, [{ ItemType, ItemCount: -1 }]); + } + + const itemId = Upgrades[itemIndex]._id.toString(); + if (!itemId) { + throw new Error("Item Id not found in upgradeMod"); + } + + if (!inventory.infiniteCredits) { + inventory.RegularCredits -= Cost; + } + if (Consumed && Consumed.length > 0) { + for (const upgrade of Consumed) { + // The client does not send the expected information about the mods, so we have to check if it's an Upgrade or RawUpgrade manually. + if (Upgrades.id(fromOid(upgrade.ItemId))) { + Upgrades.pull({ _id: upgrade.ItemId.$id }); + } else { + addMods(inventory, [ + { + ItemType: upgrade.ItemType, + ItemCount: -1 + } + ]); + } } - ]); + + itemIndex = Upgrades.findIndex(upgrade => upgrade._id.equals(itemId)); + } + + await inventory.save(); + + res.send(itemId); } - const changedInventory = await inventory.save(); - const itemId = changedInventory.toJSON().Upgrades[itemIndex].ItemId.$oid; - - if (!itemId) { - throw new Error("Item Id not found in upgradeMod"); - } - - res.send(itemId); broadcastInventoryUpdate(req); }; @@ -67,4 +130,6 @@ interface IArtifactsRequest { Cost: number; FusionPointCost: number; LegendaryFusion?: boolean; + Fingerprint?: string; + Consumed?: IUpgradeFromClient[]; } diff --git a/src/controllers/api/claimCompletedRecipeController.ts b/src/controllers/api/claimCompletedRecipeController.ts index b4325da9..fb533970 100644 --- a/src/controllers/api/claimCompletedRecipeController.ts +++ b/src/controllers/api/claimCompletedRecipeController.ts @@ -262,9 +262,9 @@ const claimCompletedRecipe = async ( "", "", "", - umbraModA.ItemId.$oid, - umbraModB.ItemId.$oid, - umbraModC.ItemId.$oid + fromOid(umbraModA.ItemId), + fromOid(umbraModB.ItemId), + fromOid(umbraModC.ItemId) ] } ], @@ -284,7 +284,18 @@ const claimCompletedRecipe = async ( "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana", { Configs: [ - { Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid] } + { + Upgrades: [ + "", + "", + "", + "", + "", + "", + fromOid(sacrificeModA.ItemId), + fromOid(sacrificeModB.ItemId) + ] + } ], XP: 450_000, Features: EquipmentFeatures.DOUBLE_CAPACITY diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts index 30dddae1..d2b361d1 100644 --- a/src/controllers/api/inventoryController.ts +++ b/src/controllers/api/inventoryController.ts @@ -6,7 +6,7 @@ import { config } from "../../services/configService.ts"; import allDialogue from "../../../static/fixed_responses/allDialogue.json" with { type: "json" }; import allPopups from "../../../static/fixed_responses/allPopups.json" with { type: "json" }; import type { ILoadoutDatabase } from "../../types/saveLoadoutTypes.ts"; -import type { IInventoryClient, IShipInventory } from "../../types/inventoryTypes/inventoryTypes.ts"; +import type { IInventoryClient, IShipInventory, IUpgradeClient } from "../../types/inventoryTypes/inventoryTypes.ts"; import { equipmentKeys } from "../../types/inventoryTypes/inventoryTypes.ts"; import type { IPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts"; import { ArtifactPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts"; @@ -30,7 +30,7 @@ import { getNemesisManifest } from "../../helpers/nemesisHelpers.ts"; import { getPersonalRooms } from "../../services/personalRoomsService.ts"; import type { IPersonalRoomsClient } from "../../types/personalRoomsTypes.ts"; import { Ship } from "../../models/shipModel.ts"; -import { toLegacyOid, toOid, version_compare } from "../../helpers/inventoryHelpers.ts"; +import { toLegacyOid, toOid, toOid2, version_compare } from "../../helpers/inventoryHelpers.ts"; import { Inbox } from "../../models/inboxModel.ts"; import { unixTimesInMs } from "../../constants/timeConstants.ts"; import { DailyDeal } from "../../models/worldStateModel.ts"; @@ -454,28 +454,140 @@ export const getInventoryResponse = async ( inventoryResponse.Nemesis = undefined; } - if (version_compare(buildLabel, "2018.02.22.14.34") < 0) { - const personalRoomsDb = await getPersonalRooms(inventory.accountOwnerId.toString()); - const personalRooms = personalRoomsDb.toJSON(); - inventoryResponse.Ship = personalRooms.Ship; + if (version_compare(buildLabel, "2019.03.07.20.21") < 0) { + // Builds before U24.4.0 handle equipment features differently + for (const category of equipmentKeys) { + for (const item of inventoryResponse[category]) { + if (item.Features && item.Features & EquipmentFeatures.DOUBLE_CAPACITY) { + item.UnlockLevel = 1; + } + if (item.Features && item.Features & EquipmentFeatures.UTILITY_SLOT) { + item.UtilityUnlocked = 1; + } + if (item.Features && item.Features & EquipmentFeatures.GILDED) { + item.Gild = true; + } + } + } - if (version_compare(buildLabel, "2016.12.21.19.13") <= 0) { - // U19.5 and below use $id instead of $oid - for (const category of equipmentKeys) { - for (const item of inventoryResponse[category]) { - toLegacyOid(item.ItemId); + if (version_compare(buildLabel, "2018.02.22.14.34") < 0) { + const personalRoomsDb = await getPersonalRooms(inventory.accountOwnerId.toString()); + const personalRooms = personalRoomsDb.toJSON(); + inventoryResponse.Ship = personalRooms.Ship; + + if (version_compare(buildLabel, "2016.12.21.19.13") <= 0) { + // U19.5 and below use $id instead of $oid + for (const category of equipmentKeys) { + for (const item of inventoryResponse[category]) { + toLegacyOid(item.ItemId); + } } - } - for (const upgrade of inventoryResponse.Upgrades) { - toLegacyOid(upgrade.ItemId); - } - if (inventoryResponse.BrandedSuits) { - for (const id of inventoryResponse.BrandedSuits) { - toLegacyOid(id); + + if (version_compare(buildLabel, "2014.02.05.00.00") < 0) { + // Pre-U12 builds store mods in an array called Cards, and have no concept of RawUpgrades + inventoryResponse.Cards = []; + for (const rawUpgrade of inventoryResponse.RawUpgrades) { + const id = inventory.RawUpgrades.find(x => x.ItemType == rawUpgrade.ItemType)?._id; + if (id) { + for (let i = 0; i < rawUpgrade.ItemCount; i++) { + const card = { + ItemType: rawUpgrade.ItemType, + ItemId: toOid2(id, buildLabel), + Rank: 0, + AmountRemaining: rawUpgrade.ItemCount + } as IUpgradeClient; + // Client doesn't see the mods unless they are in both Cards and Upgrades + inventoryResponse.Cards.push(card); + inventoryResponse.Upgrades.push(card); + } + } + } + + inventoryResponse.RawUpgrades = []; + + for (const category of equipmentKeys) { + for (const item of inventoryResponse[category]) { + for (const config of item.Configs) { + if (config.Upgrades) { + // Convert installed upgrades for U10-U11 + const convertedUpgrades: { $id: string }[] = []; + config.Upgrades.forEach(upgrade => { + const upgradeId = upgrade as string; + convertedUpgrades.push({ $id: upgradeId }); + }); + config.Upgrades = convertedUpgrades; + } + } + } + } + } + + for (const upgrade of inventoryResponse.Upgrades) { + toLegacyOid(upgrade.ItemId); + if (version_compare(buildLabel, "2016.08.19.17.12") < 0) { + // Pre-U18.18 builds use a different UpgradeFingerprint format + let rank: number = 0; + if (upgrade.UpgradeFingerprint) { + rank = Number.parseFloat( + upgrade.UpgradeFingerprint.substring( + upgrade.UpgradeFingerprint.indexOf(":") + 1, + upgrade.UpgradeFingerprint.lastIndexOf("}") + ) + ); + } + upgrade.UpgradeFingerprint = `lvl=${rank}|`; + if (version_compare(buildLabel, "2014.04.10.17.47") < 0) { + // Pre-U10 builds + if ( + !upgrade.AmountRemaining || + (upgrade.AmountRemaining && upgrade.AmountRemaining <= 0) + ) { + upgrade.AmountRemaining = 1; + } + upgrade.Rank = rank; + if (inventoryResponse.Cards) { + inventoryResponse.Cards.push(upgrade); + } + } + } + } + + if (version_compare(buildLabel, "2014.02.05.00.00") < 0) { + // Convert installed mods for pre-U12 builds + for (const category of equipmentKeys) { + for (const item of inventoryResponse[category]) { + for (const config of item.Configs) { + if (config.Upgrades) { + for (let i = 0; i < config.Upgrades.length; i++) { + const id = config.Upgrades[i] as { $id: string | undefined }; + const invUpgrade = inventoryResponse.Upgrades.find( + x => x.ItemId.$id == id.$id + ); + if (invUpgrade) { + if (id.$id?.startsWith("/Lotus")) { + // Pre-U12 builds have no concept of RawUpgrades, have to convert the db entry to the closest id of an unranked copy + id.$id = inventoryResponse.Upgrades.find( + x => x.ItemType == id.$id + )?.ItemId.$id; + } + // Pre-U10 + invUpgrade.ParentId = item.ItemId; + invUpgrade.Slot = i + 1; + } + } + } + } + } + } + } + if (inventoryResponse.BrandedSuits) { + for (const id of inventoryResponse.BrandedSuits) { + toLegacyOid(id); + } + } + if (inventoryResponse.GuildId) { + toLegacyOid(inventoryResponse.GuildId); } - } - if (inventoryResponse.GuildId) { - toLegacyOid(inventoryResponse.GuildId); } } } diff --git a/src/controllers/api/loginController.ts b/src/controllers/api/loginController.ts index f5332fff..a65d860d 100644 --- a/src/controllers/api/loginController.ts +++ b/src/controllers/api/loginController.ts @@ -161,7 +161,16 @@ const createLoginResponse = ( if (version_compare(buildLabel, "2022.09.06.19.24") >= 0) { resp.CrossPlatformAllowed = account.CrossPlatformAllowed; resp.HUB = `${myUrlBase}/api/`; - resp.MatchmakingBuildId = buildConfig.matchmakingBuildId; + + // The MatchmakingBuildId is a 64-bit integer represented as a decimal string. On live, the value is seemingly random per build, but really any value that is different across builds should work. + const [year, month, day, hour, minute] = buildLabel.split(".").map(x => parseInt(x)); + resp.MatchmakingBuildId = ( + year * 1_00_00_00_00 + + month * 1_00_00_00 + + day * 1_00_00 + + hour * 1_00 + + minute + ).toString(); } if (version_compare(buildLabel, "2023.04.25.23.40") >= 0) { if (version_compare(buildLabel, "2025.08.26.09.49") >= 0) { diff --git a/src/controllers/api/nemesisController.ts b/src/controllers/api/nemesisController.ts index 9f4af582..a8ac2413 100644 --- a/src/controllers/api/nemesisController.ts +++ b/src/controllers/api/nemesisController.ts @@ -1,4 +1,4 @@ -import { fromDbOid, toMongoDate, version_compare } from "../../helpers/inventoryHelpers.ts"; +import { fromDbOid, fromOid, toMongoDate, version_compare } from "../../helpers/inventoryHelpers.ts"; import type { IKnifeResponse } from "../../helpers/nemesisHelpers.ts"; import { antivirusMods, @@ -21,7 +21,7 @@ import { Loadout } from "../../models/inventoryModels/loadoutModel.ts"; import { addMods, freeUpSlot, getInventory } from "../../services/inventoryService.ts"; import { getAccountForRequest } from "../../services/loginService.ts"; import { SRng } from "../../services/rngService.ts"; -import type { IMongoDate, IOid } from "../../types/commonTypes.ts"; +import type { IMongoDate, IOid, IOidWithLegacySupport } from "../../types/commonTypes.ts"; import type { IEquipmentClient } from "../../types/equipmentTypes.ts"; import type { IInnateDamageFingerprint, @@ -420,7 +420,7 @@ interface IKnife { const consumeModCharge = ( response: IKnifeResponse, inventory: TInventoryDatabaseDocument, - upgrade: { ItemId: IOid; ItemType: string }, + upgrade: { ItemId: IOidWithLegacySupport; ItemType: string }, dataknifeUpgrades: string[] ): void => { response.UpgradeIds ??= []; @@ -429,13 +429,13 @@ const consumeModCharge = ( response.UpgradeNew ??= []; response.HasKnife = true; - if (upgrade.ItemId.$oid != "000000000000000000000000") { - const dbUpgrade = inventory.Upgrades.id(upgrade.ItemId.$oid)!; + if (fromOid(upgrade.ItemId) != "000000000000000000000000") { + const dbUpgrade = inventory.Upgrades.id(fromOid(upgrade.ItemId))!; const fingerprint = JSON.parse(dbUpgrade.UpgradeFingerprint!) as { lvl: number }; fingerprint.lvl += 1; dbUpgrade.UpgradeFingerprint = JSON.stringify(fingerprint); - response.UpgradeIds.push(upgrade.ItemId.$oid); + response.UpgradeIds.push(fromOid(upgrade.ItemId)); response.UpgradeTypes.push(upgrade.ItemType); response.UpgradeFingerprints.push(fingerprint); response.UpgradeNew.push(false); diff --git a/src/controllers/api/rerollRandomModController.ts b/src/controllers/api/rerollRandomModController.ts index cbed0276..0428c6df 100644 --- a/src/controllers/api/rerollRandomModController.ts +++ b/src/controllers/api/rerollRandomModController.ts @@ -1,14 +1,16 @@ import type { RequestHandler } from "express"; -import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { getAccountForRequest } from "../../services/loginService.ts"; import { addMiscItems, getInventory } from "../../services/inventoryService.ts"; import { getJSONfromString } from "../../helpers/stringHelpers.ts"; import type { RivenFingerprint } from "../../helpers/rivenHelper.ts"; import { createUnveiledRivenFingerprint, randomiseRivenStats } from "../../helpers/rivenHelper.ts"; import { ExportUpgrades } from "warframe-public-export-plus"; -import type { IOid } from "../../types/commonTypes.ts"; +import type { IOidWithLegacySupport } from "../../types/commonTypes.ts"; +import { toObjectId, toOid2 } from "../../helpers/inventoryHelpers.ts"; export const rerollRandomModController: RequestHandler = async (req, res) => { - const accountId = await getAccountIdForRequest(req); + const account = await getAccountForRequest(req); + const accountId = account._id.toString(); const request = getJSONfromString(String(req.body)); if ("ItemIds" in request) { const inventory = await getInventory(accountId, "Upgrades MiscItems"); @@ -40,7 +42,7 @@ export const rerollRandomModController: RequestHandler = async (req, res) => { } changes.push({ - ItemId: { $oid: request.ItemIds[0] }, + ItemId: toOid2(toObjectId(request.ItemIds[0]), account.BuildLabel), UpgradeFingerprint: upgrade.UpgradeFingerprint, PendingRerollFingerprint: upgrade.PendingRerollFingerprint }); @@ -76,7 +78,7 @@ interface AwDangitRequest { } interface IChange { - ItemId: IOid; + ItemId: IOidWithLegacySupport; UpgradeFingerprint?: string; PendingRerollFingerprint?: string; } diff --git a/src/controllers/api/saveLoadoutController.ts b/src/controllers/api/saveLoadoutController.ts index 60431610..2573c540 100644 --- a/src/controllers/api/saveLoadoutController.ts +++ b/src/controllers/api/saveLoadoutController.ts @@ -1,18 +1,22 @@ import type { RequestHandler } from "express"; import type { ISaveLoadoutRequest } from "../../types/saveLoadoutTypes.ts"; import { handleInventoryItemConfigChange } from "../../services/saveLoadoutService.ts"; -import { getAccountIdForRequest } from "../../services/loginService.ts"; +import { getAccountForRequest } from "../../services/loginService.ts"; import { getJSONfromString } from "../../helpers/stringHelpers.ts"; export const saveLoadoutController: RequestHandler = async (req, res) => { - const accountId = await getAccountIdForRequest(req); + const account = await getAccountForRequest(req); const body: ISaveLoadoutRequest = getJSONfromString(String(req.body)); // console.log(util.inspect(body, { showHidden: false, depth: null, colors: true })); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { UpgradeVer, ...equipmentChanges } = body; - const newLoadoutId = await handleInventoryItemConfigChange(equipmentChanges, accountId); + const newLoadoutId = await handleInventoryItemConfigChange( + equipmentChanges, + account._id.toString(), + account.BuildLabel + ); //send back new loadout id, if new loadout was added if (newLoadoutId) { diff --git a/src/controllers/api/upgradesController.ts b/src/controllers/api/upgradesController.ts index cccb2cd9..06b71584 100644 --- a/src/controllers/api/upgradesController.ts +++ b/src/controllers/api/upgradesController.ts @@ -1,155 +1,288 @@ import type { RequestHandler } from "express"; -import type { IUpgradesRequest } from "../../types/requestTypes.ts"; +import { fromOid, version_compare } from "../../helpers/inventoryHelpers.ts"; +import type { IUpgradesRequest, IUpgradesRequestLegacy } from "../../types/requestTypes.ts"; import type { ArtifactPolarity, IAbilityOverride } from "../../types/inventoryTypes/commonInventoryTypes.ts"; import type { IInventoryClient, IMiscItem } from "../../types/inventoryTypes/inventoryTypes.ts"; -import { getAccountIdForRequest } from "../../services/loginService.ts"; -import { addMiscItems, addRecipes, getInventory, updateCurrency } from "../../services/inventoryService.ts"; +import { getAccountForRequest } from "../../services/loginService.ts"; +import { addMiscItems, addMods, addRecipes, getInventory, updateCurrency } from "../../services/inventoryService.ts"; import { getRecipeByResult } from "../../services/itemDataService.ts"; import type { IInventoryChanges } from "../../types/purchaseTypes.ts"; import { addInfestedFoundryXP, applyCheatsToInfestedFoundry } from "../../services/infestedFoundryService.ts"; import { sendWsBroadcastTo } from "../../services/wsService.ts"; import type { IEquipmentDatabase } from "../../types/equipmentTypes.ts"; import { EquipmentFeatures } from "../../types/equipmentTypes.ts"; +import { Types } from "mongoose"; export const upgradesController: RequestHandler = async (req, res) => { - const accountId = await getAccountIdForRequest(req); - const payload = JSON.parse(String(req.body)) as IUpgradesRequest; + const account = await getAccountForRequest(req); + const accountId = account._id.toString(); const inventory = await getInventory(accountId); const inventoryChanges: IInventoryChanges = {}; - for (const operation of payload.Operations) { - if ( - operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" || - operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker" - ) { - updateCurrency(inventory, 10, true); - } else if ( - operation.OperationType != "UOT_SWAP_POLARITY" && - operation.OperationType != "UOT_ABILITY_OVERRIDE" - ) { - if (!operation.UpgradeRequirement) { - throw new Error(`${operation.OperationType} operation should be free?`); - } - addMiscItems(inventory, [ - { - ItemType: operation.UpgradeRequirement, - ItemCount: -1 - } satisfies IMiscItem - ]); - } - if (operation.OperationType == "UOT_ABILITY_OVERRIDE") { - console.assert(payload.ItemCategory == "Suits"); - const suit = inventory.Suits.id(payload.ItemId.$oid)!; - - let newAbilityOverride: IAbilityOverride | undefined; - let totalPercentagePointsConsumed = 0; - if (operation.UpgradeRequirement != "") { - newAbilityOverride = { - Ability: operation.UpgradeRequirement, - Index: operation.PolarizeSlot - }; - - const recipe = getRecipeByResult(operation.UpgradeRequirement)!; - for (const ingredient of recipe.ingredients) { - totalPercentagePointsConsumed += ingredient.ItemCount / 10; - if (!inventory.infiniteHelminthMaterials) { - inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -= - ingredient.ItemCount; + if (account.BuildLabel && version_compare(account.BuildLabel, "2019.03.07.20.21") < 0) { + // Builds before U24.4.0 have a different request format + const payload = JSON.parse(String(req.body)) as IUpgradesRequestLegacy; + const itemId = fromOid(payload.Weapon.ItemId); + if (itemId) { + if (payload.IsSwappingOperation === true) { + const item = inventory[payload.Category].id(itemId)!; + for (let i = 0; i != payload.PolarityRemap.length; ++i) { + // Can't really be selective here like the newer format, it pushes everything in a way that the comparison fails against... + setSlotPolarity(item, i, payload.PolarityRemap[i].Value); + } + } else { + if (payload.PolarizeReq) { + switch (payload.PolarizeReq) { + case "/Lotus/Types/Items/MiscItems/Forma": + case "/Lotus/Types/Items/MiscItems/FormaUmbra": { + const item = inventory[payload.Category].id(itemId)!; + item.XP = 0; + setSlotPolarity(item, payload.PolarizeSlot, payload.PolarizeValue); + item.Polarized ??= 0; + item.Polarized += 1; + sendWsBroadcastTo(accountId, { update_inventory: true }); + break; + } + default: + throw new Error("Unsupported polarize item: " + payload.PolarizeReq); } + addMiscItems(inventory, [ + { + ItemType: payload.PolarizeReq, + ItemCount: -1 + } satisfies IMiscItem + ]); } - } - - for (const entry of operation.PolarityRemap) { - suit.Configs[entry.Slot] ??= {}; - suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride; - } - - const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, totalPercentagePointsConsumed * 8); - addRecipes(inventory, recipeChanges); - - inventoryChanges.Recipes = recipeChanges; - inventoryChanges.InfestedFoundry = inventory.toJSON().InfestedFoundry; - applyCheatsToInfestedFoundry(inventory, inventoryChanges.InfestedFoundry!); - } else - switch (operation.UpgradeRequirement) { - case "/Lotus/Types/Items/MiscItems/OrokinReactor": - case "/Lotus/Types/Items/MiscItems/OrokinCatalyst": { - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - item.Features ??= 0; - item.Features |= EquipmentFeatures.DOUBLE_CAPACITY; - break; - } - case "/Lotus/Types/Items/MiscItems/UtilityUnlocker": - case "/Lotus/Types/Items/MiscItems/WeaponUtilityUnlocker": { - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - item.Features ??= 0; - item.Features |= EquipmentFeatures.UTILITY_SLOT; - break; - } - case "/Lotus/Types/Items/MiscItems/HeavyWeaponCatalyst": { - console.assert(payload.ItemCategory == "SpaceGuns"); - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - item.Features ??= 0; - item.Features |= EquipmentFeatures.GRAVIMAG_INSTALLED; - break; - } - case "/Lotus/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker": - case "/Lotus/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker": - case "/Lotus/Types/Items/MiscItems/WeaponMeleeArcaneUnlocker": - case "/Lotus/Types/Items/MiscItems/WeaponAmpArcaneUnlocker": - case "/Lotus/Types/Items/MiscItems/WeaponArchGunArcaneUnlocker": { - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - item.Features ??= 0; - if (operation.OperationType == "UOT_ARCANE_UNLOCK_1") { - item.Features |= EquipmentFeatures.SECOND_ARCANE_SLOT; - } else { - item.Features |= EquipmentFeatures.ARCANE_SLOT; + if (payload.UtilityReq) { + switch (payload.UtilityReq) { + case "/Lotus/Types/Items/MiscItems/UtilityUnlocker": { + const item = inventory[payload.Category].id(itemId)!; + item.Features ??= 0; + item.Features |= EquipmentFeatures.UTILITY_SLOT; + break; + } + default: + throw new Error("Unsupported utility item: " + payload.UtilityReq); } - break; + addMiscItems(inventory, [ + { + ItemType: payload.UtilityReq, + ItemCount: -1 + } satisfies IMiscItem + ]); } - case "/Lotus/Types/Items/MiscItems/ValenceAdapter": { - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - item.Features ??= 0; - item.Features |= EquipmentFeatures.VALENCE_SWAP; - break; + if (payload.UpgradeReq) { + switch (payload.UpgradeReq) { + case "/Lotus/Types/Items/MiscItems/OrokinReactor": + case "/Lotus/Types/Items/MiscItems/OrokinCatalyst": { + const item = inventory[payload.Category].id(itemId)!; + item.Features ??= 0; + item.Features |= EquipmentFeatures.DOUBLE_CAPACITY; + break; + } + default: + throw new Error("Unsupported upgrade: " + payload.UpgradeReq); + } + addMiscItems(inventory, [ + { + ItemType: payload.UpgradeReq, + ItemCount: -1 + } satisfies IMiscItem + ]); } - case "/Lotus/Types/Items/MiscItems/Forma": - case "/Lotus/Types/Items/MiscItems/FormaUmbra": - case "/Lotus/Types/Items/MiscItems/FormaAura": - case "/Lotus/Types/Items/MiscItems/FormaStance": { - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - item.XP = 0; - setSlotPolarity(item, operation.PolarizeSlot, operation.PolarizeValue); - item.Polarized ??= 0; - item.Polarized += 1; - sendWsBroadcastTo(accountId, { update_inventory: true }); // webui may need to to re-add "max rank" button - break; + } + + // Handle attaching/detaching mods in U7-U8 + if (payload.UpgradesToAttach && payload.UpgradesToAttach.length > 0) { + const item = inventory[payload.Category].id(itemId)!; + if (!item.Configs[0]) { + item.Configs.push({ Upgrades: ["", "", "", "", "", "", "", "", "", "", ""] }); } - case "/Lotus/Types/Items/MiscItems/ModSlotUnlocker": { - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - item.ModSlotPurchases ??= 0; - item.ModSlotPurchases += 1; - break; + if (item.Configs[0].Upgrades && item.Configs[0].Upgrades.length < 11) { + item.Configs[0].Upgrades.length = 11; } - case "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker": { - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - item.CustomizationSlotPurchases ??= 0; - item.CustomizationSlotPurchases += 1; - break; - } - case "": { - console.assert(operation.OperationType == "UOT_SWAP_POLARITY"); - const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; - for (let i = 0; i != operation.PolarityRemap.length; ++i) { - if (operation.PolarityRemap[i].Slot != i) { - setSlotPolarity(item, i, operation.PolarityRemap[i].Value); + payload.UpgradesToAttach.forEach(upgrade => { + if (item.Configs[0].Upgrades && upgrade.ItemId.$id && upgrade.Slot) { + const arr = item.Configs[0].Upgrades; + if (arr.indexOf(upgrade.ItemId.$id) != -1) { + // Handle swapping mod to a different slot + arr[arr.indexOf(upgrade.ItemId.$id)] = ""; + } + // We need to convert RawUpgrade into Upgrade once it's attached + const rawUpgrade = inventory.RawUpgrades.id(upgrade.ItemId.$id); + if (rawUpgrade) { + const newId = new Types.ObjectId().toString(); + arr[upgrade.Slot - 1] = newId; + addMods(inventory, [ + { + ItemType: upgrade.ItemType, + ItemCount: -1 + } + ]); + inventory.Upgrades.push({ + UpgradeFingerprint: `{"lvl":0}`, + ItemType: upgrade.ItemType, + _id: newId + }); + } else { + arr[upgrade.Slot - 1] = upgrade.ItemId.$id; } } - break; - } - default: - throw new Error("Unsupported upgrade: " + operation.UpgradeRequirement); + }); } + if (payload.UpgradesToDetach && payload.UpgradesToDetach.length > 0) { + const item = inventory[payload.Category].id(itemId)!; + if (item.Configs[0].Upgrades && item.Configs[0].Upgrades.length < 11) { + item.Configs[0].Upgrades.length = 11; + } + payload.UpgradesToDetach.forEach(upgrade => { + if (item.Configs[0].Upgrades && upgrade.ItemId.$id) { + const arr = item.Configs[0].Upgrades; + arr[arr.indexOf(upgrade.ItemId.$id)] = ""; + } + }); + } + } + } else { + const payload = JSON.parse(String(req.body)) as IUpgradesRequest; + for (const operation of payload.Operations) { + if ( + operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" || + operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker" + ) { + updateCurrency(inventory, 10, true); + } else if ( + operation.OperationType != "UOT_SWAP_POLARITY" && + operation.OperationType != "UOT_ABILITY_OVERRIDE" + ) { + if (!operation.UpgradeRequirement) { + throw new Error(`${operation.OperationType} operation should be free?`); + } + addMiscItems(inventory, [ + { + ItemType: operation.UpgradeRequirement, + ItemCount: -1 + } satisfies IMiscItem + ]); + } + + if (operation.OperationType == "UOT_ABILITY_OVERRIDE") { + console.assert(payload.ItemCategory == "Suits"); + const suit = inventory.Suits.id(payload.ItemId.$oid)!; + + let newAbilityOverride: IAbilityOverride | undefined; + let totalPercentagePointsConsumed = 0; + if (operation.UpgradeRequirement != "") { + newAbilityOverride = { + Ability: operation.UpgradeRequirement, + Index: operation.PolarizeSlot + }; + + const recipe = getRecipeByResult(operation.UpgradeRequirement)!; + for (const ingredient of recipe.ingredients) { + totalPercentagePointsConsumed += ingredient.ItemCount / 10; + if (!inventory.infiniteHelminthMaterials) { + inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -= + ingredient.ItemCount; + } + } + } + + for (const entry of operation.PolarityRemap) { + suit.Configs[entry.Slot] ??= {}; + suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride; + } + + const recipeChanges = addInfestedFoundryXP( + inventory.InfestedFoundry!, + totalPercentagePointsConsumed * 8 + ); + addRecipes(inventory, recipeChanges); + + inventoryChanges.Recipes = recipeChanges; + inventoryChanges.InfestedFoundry = inventory.toJSON().InfestedFoundry; + applyCheatsToInfestedFoundry(inventory, inventoryChanges.InfestedFoundry!); + } else + switch (operation.UpgradeRequirement) { + case "/Lotus/Types/Items/MiscItems/OrokinReactor": + case "/Lotus/Types/Items/MiscItems/OrokinCatalyst": { + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + item.Features ??= 0; + item.Features |= EquipmentFeatures.DOUBLE_CAPACITY; + break; + } + case "/Lotus/Types/Items/MiscItems/UtilityUnlocker": + case "/Lotus/Types/Items/MiscItems/WeaponUtilityUnlocker": { + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + item.Features ??= 0; + item.Features |= EquipmentFeatures.UTILITY_SLOT; + break; + } + case "/Lotus/Types/Items/MiscItems/HeavyWeaponCatalyst": { + console.assert(payload.ItemCategory == "SpaceGuns"); + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + item.Features ??= 0; + item.Features |= EquipmentFeatures.GRAVIMAG_INSTALLED; + break; + } + case "/Lotus/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker": + case "/Lotus/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker": + case "/Lotus/Types/Items/MiscItems/WeaponMeleeArcaneUnlocker": + case "/Lotus/Types/Items/MiscItems/WeaponAmpArcaneUnlocker": + case "/Lotus/Types/Items/MiscItems/WeaponArchGunArcaneUnlocker": { + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + item.Features ??= 0; + if (operation.OperationType == "UOT_ARCANE_UNLOCK_1") { + item.Features |= EquipmentFeatures.SECOND_ARCANE_SLOT; + } else { + item.Features |= EquipmentFeatures.ARCANE_SLOT; + } + break; + } + case "/Lotus/Types/Items/MiscItems/ValenceAdapter": { + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + item.Features ??= 0; + item.Features |= EquipmentFeatures.VALENCE_SWAP; + break; + } + case "/Lotus/Types/Items/MiscItems/Forma": + case "/Lotus/Types/Items/MiscItems/FormaUmbra": + case "/Lotus/Types/Items/MiscItems/FormaAura": + case "/Lotus/Types/Items/MiscItems/FormaStance": { + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + item.XP = 0; + setSlotPolarity(item, operation.PolarizeSlot, operation.PolarizeValue); + item.Polarized ??= 0; + item.Polarized += 1; + sendWsBroadcastTo(accountId, { update_inventory: true }); // webui may need to to re-add "max rank" button + break; + } + case "/Lotus/Types/Items/MiscItems/ModSlotUnlocker": { + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + item.ModSlotPurchases ??= 0; + item.ModSlotPurchases += 1; + break; + } + case "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker": { + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + item.CustomizationSlotPurchases ??= 0; + item.CustomizationSlotPurchases += 1; + break; + } + case "": { + console.assert(operation.OperationType == "UOT_SWAP_POLARITY"); + const item = inventory[payload.ItemCategory].id(payload.ItemId.$oid)!; + for (let i = 0; i != operation.PolarityRemap.length; ++i) { + if (operation.PolarityRemap[i].Slot != i) { + setSlotPolarity(item, i, operation.PolarityRemap[i].Value); + } + } + break; + } + default: + throw new Error("Unsupported upgrade: " + operation.UpgradeRequirement); + } + } } await inventory.save(); res.json({ InventoryChanges: inventoryChanges }); diff --git a/src/controllers/custom/importController.ts b/src/controllers/custom/importController.ts index 47a850f6..98ebc1fe 100644 --- a/src/controllers/custom/importController.ts +++ b/src/controllers/custom/importController.ts @@ -13,33 +13,41 @@ export const importController: RequestHandler = async (req, res) => { const request = req.body as IImportRequest; let anyKnownKey = false; + try { + const inventory = await getInventory(accountId); + if (importInventory(inventory, request.inventory)) { + anyKnownKey = true; + await inventory.save(); + } - const inventory = await getInventory(accountId); - if (importInventory(inventory, request.inventory)) { - anyKnownKey = true; - await inventory.save(); + if ("LoadOutPresets" in request.inventory && request.inventory.LoadOutPresets) { + anyKnownKey = true; + const loadout = await getLoadout(accountId); + importLoadOutPresets(loadout, request.inventory.LoadOutPresets); + await loadout.save(); + } + + if ( + request.inventory.Ship?.Rooms || // very old accounts may have Ship with { Features: [ ... ] } + "Apartment" in request.inventory || + "TailorShop" in request.inventory + ) { + anyKnownKey = true; + const personalRooms = await getPersonalRooms(accountId); + importPersonalRooms(personalRooms, request.inventory); + await personalRooms.save(); + } + + if (!anyKnownKey) { + res.send("noKnownKey").end(); + } + + broadcastInventoryUpdate(req); + } catch (e) { + console.error(e); + res.send((e as Error).message); } - - if ("LoadOutPresets" in request.inventory && request.inventory.LoadOutPresets) { - anyKnownKey = true; - const loadout = await getLoadout(accountId); - importLoadOutPresets(loadout, request.inventory.LoadOutPresets); - await loadout.save(); - } - - if ( - request.inventory.Ship?.Rooms || // very old accounts may have Ship with { Features: [ ... ] } - "Apartment" in request.inventory || - "TailorShop" in request.inventory - ) { - anyKnownKey = true; - const personalRooms = await getPersonalRooms(accountId); - importPersonalRooms(personalRooms, request.inventory); - await personalRooms.save(); - } - - res.json(anyKnownKey); - broadcastInventoryUpdate(req); + res.end(); }; interface IImportRequest { diff --git a/src/helpers/inventoryHelpers.ts b/src/helpers/inventoryHelpers.ts index a5edcaf8..24e2d2eb 100644 --- a/src/helpers/inventoryHelpers.ts +++ b/src/helpers/inventoryHelpers.ts @@ -20,6 +20,10 @@ export const version_compare = (a: string, b: string): number => { return 0; }; +export const toObjectId = (s: string): Types.ObjectId => { + return new Types.ObjectId(s); +}; + export const toOid = (objectId: Types.ObjectId): IOid => { return { $oid: objectId.toString() }; }; diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index 162491e2..21f65c58 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -1060,7 +1060,6 @@ const EquipmentSchema = new Schema( InfestationDays: Number, InfestationType: String, ModularParts: { type: [String], default: undefined }, - UnlockLevel: Number, Expiry: Date, SkillTree: String, OffensiveUpgrade: String, @@ -1493,6 +1492,8 @@ const inventorySchema = new Schema( relicRewardItemCountMultiplier: { type: Number, default: 1 }, nightwaveStandingMultiplier: { type: Number, default: 1 }, + Created: Date, + SubscribedToEmails: { type: Number, default: 0 }, SubscribedToEmailsPersonalized: { type: Number, default: 0 }, RewardSeed: BigInt, diff --git a/src/routes/stats.ts b/src/routes/stats.ts index ad8d6879..35a0d88d 100644 --- a/src/routes/stats.ts +++ b/src/routes/stats.ts @@ -9,6 +9,7 @@ statsRouter.get("/view.php", viewController); statsRouter.get("/profileStats.php", viewController); statsRouter.get("/leaderboard.php", leaderboardGetController); statsRouter.post("/upload.php", uploadController); +statsRouter.post("/view.php", viewController); statsRouter.post("/leaderboardWeekly.php", leaderboardPostController); statsRouter.post("/leaderboardArchived.php", leaderboardPostController); diff --git a/src/services/buildConfigService.ts b/src/services/buildConfigService.ts index 81c76cc4..53abc492 100644 --- a/src/services/buildConfigService.ts +++ b/src/services/buildConfigService.ts @@ -5,13 +5,11 @@ import { repoDir } from "../helpers/pathHelper.ts"; interface IBuildConfig { version: string; buildLabel: string; - matchmakingBuildId: string; } export const buildConfig: IBuildConfig = { version: "", - buildLabel: "", - matchmakingBuildId: "" + buildLabel: "" }; const buildConfigPath = path.join(repoDir, "static/data/buildConfig.json"); diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 2728744c..761b3d5b 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -33,7 +33,7 @@ import type { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/generic import type { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes.ts"; import { logger } from "../utils/logger.ts"; import { convertInboxMessage, fromStoreItem, getKeyChainItems } from "./itemDataService.ts"; -import type { IFlavourItem, IItemConfig } from "../types/inventoryTypes/commonInventoryTypes.ts"; +import type { IFlavourItem, IItemConfig, IItemConfigDatabase } from "../types/inventoryTypes/commonInventoryTypes.ts"; import type { IDefaultUpgrade, IPowersuit, ISentinel, TStandingLimitBin } from "warframe-public-export-plus"; import { ExportArcanes, @@ -922,9 +922,9 @@ export const addItems = async ( export const applyDefaultUpgrades = ( inventory: TInventoryDatabaseDocument, defaultUpgrades: IDefaultUpgrade[] | undefined -): IItemConfig[] => { +): IItemConfigDatabase[] => { const modsToGive: IRawUpgrade[] = []; - const configs: IItemConfig[] = []; + const configs: IItemConfigDatabase[] = []; if (defaultUpgrades) { const upgrades = []; for (const defaultUpgrade of defaultUpgrades) { diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index 58d57755..367668c4 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -84,7 +84,7 @@ import { import { config } from "./configService.ts"; import libraryDailyTasks from "../../static/fixed_responses/libraryDailyTasks.json" with { type: "json" }; import type { IGoal, ISyndicateMissionInfo } from "../types/worldStateTypes.ts"; -import { fromOid } from "../helpers/inventoryHelpers.ts"; +import { fromOid, version_compare } from "../helpers/inventoryHelpers.ts"; import type { TAccountDocument } from "./loginService.ts"; import type { ITypeCount } from "../types/commonTypes.ts"; import type { IEquipmentClient } from "../types/equipmentTypes.ts"; @@ -480,9 +480,32 @@ export const addMissionInventoryUpdates = async ( case "Upgrades": value.forEach(clientUpgrade => { const id = fromOid(clientUpgrade.ItemId); - if (id == "") { + // Really old builds (tested U7-U8) do not have the UpgradeFingerprint set for unranked mod drops + clientUpgrade.UpgradeFingerprint ??= "lvl=0|"; + // U11 and below also don't initialize ItemCount since RawUpgrade doesn't exist in them + clientUpgrade.ItemCount ??= 1; + if (account.BuildLabel && version_compare(account.BuildLabel, "2016.08.19.17.12") < 0) { + // Acquired Mods have a different UpgradeFingerprint format in pre-U18.18.0 builds, this converts them to the format the database expects + clientUpgrade.UpgradeFingerprint = `{"lvl":${clientUpgrade.UpgradeFingerprint.substring( + clientUpgrade.UpgradeFingerprint.indexOf("=") + 1, + clientUpgrade.UpgradeFingerprint.lastIndexOf("|") + )}}`; + } + // Handle Fusion Core drops + const parsedFingerprint = JSON.parse(clientUpgrade.UpgradeFingerprint) as { lvl: number }; + if (parsedFingerprint.lvl != 0) { + inventory.Upgrades.push({ + ItemType: clientUpgrade.ItemType, + UpgradeFingerprint: clientUpgrade.UpgradeFingerprint + }); + } else if (id == "") { // U19 does not provide RawUpgrades and instead interleaves them with riven progress here - addMods(inventory, [clientUpgrade]); + addMods(inventory, [ + { + ItemType: clientUpgrade.ItemType, + ItemCount: clientUpgrade.ItemCount + } + ]); } else { const upgrade = inventory.Upgrades.id(id)!; upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress diff --git a/src/services/questService.ts b/src/services/questService.ts index 10eff9c6..b13732cc 100644 --- a/src/services/questService.ts +++ b/src/services/questService.ts @@ -15,6 +15,7 @@ import type { IQuestKeyClient, IQuestKeyDatabase, IQuestStage } from "../types/i import { logger } from "../utils/logger.ts"; import { ExportKeys, ExportRecipes } from "warframe-public-export-plus"; import { addFixedLevelRewards } from "./missionInventoryUpdateService.ts"; +import { fromOid } from "../helpers/inventoryHelpers.ts"; import type { IInventoryChanges } from "../types/purchaseTypes.ts"; import questCompletionItems from "../../static/fixed_responses/questCompletionRewards.json" with { type: "json" }; import type { ITypeCount } from "../types/commonTypes.ts"; @@ -761,9 +762,9 @@ export const removeRequiredItems = async (inventory: TInventoryDatabaseDocument, "", "", "", - umbraModA.ItemId.$oid, - umbraModB.ItemId.$oid, - umbraModC.ItemId.$oid + fromOid(umbraModA.ItemId), + fromOid(umbraModB.ItemId), + fromOid(umbraModC.ItemId) ] } ], @@ -778,7 +779,16 @@ export const removeRequiredItems = async (inventory: TInventoryDatabaseDocument, addEquipment(inventory, "Melee", "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana", { Configs: [ { - Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid] + Upgrades: [ + "", + "", + "", + "", + "", + "", + fromOid(sacrificeModA.ItemId), + fromOid(sacrificeModB.ItemId) + ] } ], XP: 450_000, diff --git a/src/services/saveLoadoutService.ts b/src/services/saveLoadoutService.ts index 506a5444..5a7f64ca 100644 --- a/src/services/saveLoadoutService.ts +++ b/src/services/saveLoadoutService.ts @@ -6,14 +6,15 @@ import type { ISaveLoadoutRequestNoUpgradeVer } from "../types/saveLoadoutTypes.ts"; import { Loadout } from "../models/inventoryModels/loadoutModel.ts"; -import { getInventory } from "./inventoryService.ts"; +import { addMods, getInventory } from "./inventoryService.ts"; import type { IOid } from "../types/commonTypes.ts"; import { Types } from "mongoose"; import { isEmptyObject } from "../helpers/general.ts"; +import { version_compare } from "../helpers/inventoryHelpers.ts"; import { logger } from "../utils/logger.ts"; import type { TEquipmentKey } from "../types/inventoryTypes/inventoryTypes.ts"; import { equipmentKeys } from "../types/inventoryTypes/inventoryTypes.ts"; -import type { IItemConfig } from "../types/inventoryTypes/commonInventoryTypes.ts"; +import type { IItemConfig, IItemConfigDatabase } from "../types/inventoryTypes/commonInventoryTypes.ts"; import { importCrewShipMembers, importCrewShipWeapon, importLoadOutConfig } from "./importService.ts"; //TODO: setup default items on account creation or like originally in giveStartingItems.php @@ -26,7 +27,8 @@ itemconfig has multiple config ids */ export const handleInventoryItemConfigChange = async ( equipmentChanges: ISaveLoadoutRequestNoUpgradeVer, - accountId: string + accountId: string, + buildLabel: string | undefined ): Promise => { const inventory = await getInventory(accountId); @@ -196,7 +198,36 @@ export const handleInventoryItemConfigChange = async ( for (const [configId, config] of Object.entries(itemConfigEntries)) { if (/^[0-9]+$/.test(configId)) { - inventoryItem.Configs[parseInt(configId)] = config as IItemConfig; + const c = config as IItemConfig; + if (buildLabel && version_compare(buildLabel, "2014.04.10.17.47") < 0) { + if (c.Upgrades) { + // U10-U11 store mods in the item config as $id instead of a string, need to convert that here + const convertedUpgrades: string[] = []; + c.Upgrades.forEach(upgrade => { + const upgradeId = upgrade as { $id: string }; + const rawUpgrade = inventory.RawUpgrades.id(upgradeId.$id); + if (rawUpgrade) { + const newId = new Types.ObjectId(); + convertedUpgrades.push(newId.toString()); + addMods(inventory, [ + { + ItemType: rawUpgrade.ItemType, + ItemCount: -1 + } + ]); + inventory.Upgrades.push({ + UpgradeFingerprint: `{"lvl":0}`, + ItemType: rawUpgrade.ItemType, + _id: newId + }); + } else { + convertedUpgrades.push(upgradeId.$id); + } + }); + c.Upgrades = convertedUpgrades; + } + } + inventoryItem.Configs[parseInt(configId)] = c as IItemConfigDatabase; } } if ("Favorite" in itemConfigEntries) { diff --git a/src/types/equipmentTypes.ts b/src/types/equipmentTypes.ts index f72dcca3..da1fad5e 100644 --- a/src/types/equipmentTypes.ts +++ b/src/types/equipmentTypes.ts @@ -4,6 +4,7 @@ import type { ICrewShipCustomization, IFlavourItem, IItemConfig, + IItemConfigDatabase, IPolarity } from "./inventoryTypes/commonInventoryTypes.ts"; @@ -33,7 +34,7 @@ export enum EquipmentFeatures { export interface IEquipmentDatabase { ItemType: string; ItemName?: string; - Configs: IItemConfig[]; + Configs: IItemConfigDatabase[]; UpgradeVer?: number; XP?: number; Features?: number; @@ -48,7 +49,6 @@ export interface IEquipmentDatabase { InfestationDays?: number; InfestationType?: string; ModularParts?: string[]; - UnlockLevel?: number; Expiry?: Date; SkillTree?: string; OffensiveUpgrade?: string; @@ -69,9 +69,18 @@ export interface IEquipmentDatabase { export interface IEquipmentClient extends Omit< IEquipmentDatabase, - "_id" | "InfestationDate" | "Expiry" | "UpgradesExpiry" | "UmbraDate" | "Weapon" | "CrewMembers" | "Details" + | "_id" + | "Configs" + | "InfestationDate" + | "Expiry" + | "UpgradesExpiry" + | "UmbraDate" + | "Weapon" + | "CrewMembers" + | "Details" > { ItemId: IOidWithLegacySupport; + Configs: IItemConfig[]; InfestationDate?: IMongoDate; Expiry?: IMongoDate; UpgradesExpiry?: IMongoDate; @@ -79,6 +88,10 @@ export interface IEquipmentClient Weapon?: ICrewShipWeaponClient; CrewMembers?: ICrewShipMembersClient; Details?: IKubrowPetDetailsClient; + // For Pre-U24.4.0 builds + UnlockLevel?: number; + UtilityUnlocked?: number; + Gild?: boolean; } export interface IArchonCrystalUpgrade { diff --git a/src/types/inventoryTypes/commonInventoryTypes.ts b/src/types/inventoryTypes/commonInventoryTypes.ts index f0d139f5..88defde6 100644 --- a/src/types/inventoryTypes/commonInventoryTypes.ts +++ b/src/types/inventoryTypes/commonInventoryTypes.ts @@ -47,7 +47,7 @@ export interface IItemConfig { facial?: IColor; syancol?: IColor; cloth?: IColor; - Upgrades?: string[]; + Upgrades?: string[] | { $id: string }[]; Name?: string; OperatorAmp?: IOid; Songs?: ISong[]; @@ -56,13 +56,17 @@ export interface IItemConfig { ugly?: boolean; } +export interface IItemConfigDatabase extends Omit { + Upgrades?: string[]; +} + export interface ISong { m?: string; b?: string; p?: string; s: string; } -export interface IOperatorConfigDatabase extends IItemConfig { +export interface IOperatorConfigDatabase extends IItemConfigDatabase { _id: Types.ObjectId; } diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 50457383..c720e6e9 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -318,6 +318,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu Accolades?: IAccolades; Counselor?: boolean; Upgrades: IUpgradeClient[]; + Cards?: IUpgradeClient[]; // U8 EquippedGear: string[]; DeathMarks: string[]; FusionTreasures: IFusionTreasure[]; @@ -592,10 +593,16 @@ export interface IUpgradeClient { ItemType: string; UpgradeFingerprint?: string; PendingRerollFingerprint?: string; - ItemId: IOid; + ItemId: IOidWithLegacySupport; + // Stuff for U7-U8 + ParentId?: IOidWithLegacySupport; + Slot?: number; + AmountRemaining?: number; + Rank?: number; } -export interface IUpgradeDatabase extends Omit { +export interface IUpgradeDatabase + extends Omit { _id: Types.ObjectId; } @@ -603,9 +610,9 @@ export interface IUpgradeFromClient { ItemType: string; ItemId: IOidWithLegacySupport; FromSKU?: boolean; - UpgradeFingerprint: string; + UpgradeFingerprint?: string; PendingRerollFingerprint: string; - ItemCount: number; + ItemCount?: number; LastAdded: IOidWithLegacySupport; } diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts index ed82d296..c5f71f32 100644 --- a/src/types/requestTypes.ts +++ b/src/types/requestTypes.ts @@ -1,4 +1,4 @@ -import type { IOid, ITypeCount } from "./commonTypes.ts"; +import type { IOid, IOidWithLegacySupport, ITypeCount } from "./commonTypes.ts"; import type { ArtifactPolarity, IPolarity } from "./inventoryTypes/commonInventoryTypes.ts"; import type { IBooster, @@ -21,7 +21,8 @@ import type { IInvasionProgressClient, IWeaponSkinClient, IKubrowPetEggClient, - INemesisClient + INemesisClient, + IUpgradeClient } from "./inventoryTypes/inventoryTypes.ts"; import type { IGroup } from "./loginTypes.ts"; import type { ILoadOutPresets } from "./saveLoadoutTypes.ts"; @@ -227,6 +228,24 @@ export interface IUpgradesRequest { UpgradeVersion: number; Operations: IUpgradeOperation[]; } +export interface IUpgradesRequestLegacy { + Category: TEquipmentKey; + Weapon: { ItemType: string; ItemId: IOidWithLegacySupport }; + UpgradeVer: number; + UnlockLevel: number; + Polarized: number; + UtilityUnlocked: number; + FocusLens?: string; + UpgradeReq?: string; + UtilityReq?: string; + IsSwappingOperation: boolean; + PolarizeReq?: string; + PolarizeSlot: number; + PolarizeValue: ArtifactPolarity; + PolarityRemap: IPolarity[]; + UpgradesToAttach?: IUpgradeClient[]; + UpgradesToDetach?: IUpgradeClient[]; +} export interface IUpgradeOperation { OperationType: string; UpgradeRequirement: string; // uniqueName of item being consumed diff --git a/src/types/session.ts b/src/types/session.ts index 056cc150..29dced76 100644 --- a/src/types/session.ts +++ b/src/types/session.ts @@ -31,7 +31,7 @@ export interface ISession { export interface IFindSessionRequest { id?: string; originalSessionId?: string; - buildId?: number; + buildId?: number | bigint; gameModeId?: number; regionId?: number; maxEloDifference?: number; diff --git a/static/webui/script.js b/static/webui/script.js index 014d7be3..473b07ba 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -426,9 +426,25 @@ function fetchItemList() { }; // Add mods missing in data sources + data.mods.push({ + uniqueName: "/Lotus/Upgrades/Mods/Fusers/CommonModFuser", + name: loc("code_fusionCoreCommon"), + fusionLimit: 3 + }); + data.mods.push({ + uniqueName: "/Lotus/Upgrades/Mods/Fusers/UncommonModFuser", + name: loc("code_fusionCoreUncommon"), + fusionLimit: 5 + }); + data.mods.push({ + uniqueName: "/Lotus/Upgrades/Mods/Fusers/RareModFuser", + name: loc("code_fusionCoreRare"), + fusionLimit: 5 + }); data.mods.push({ uniqueName: "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser", - name: loc("code_legendaryCore") + name: loc("code_legendaryCore"), + fusionLimit: 0 }); data.mods.push({ uniqueName: "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod", @@ -1507,7 +1523,9 @@ function updateInventory() { { const td = document.createElement("td"); td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType; - td.innerHTML += " ★ 0/" + maxRank + ""; + if (maxRank > 0) { + td.innerHTML += " ★ 0/" + maxRank + ""; + } if (item.ItemCount > 1) { td.innerHTML += " 🗍 " + parseInt(item.ItemCount) + ""; @@ -3463,6 +3481,9 @@ function doAddAllMods() { for (const child of document.getElementById("datalist-mods").children) { modsAll.add(child.getAttribute("data-key")); } + modsAll.delete("/Lotus/Upgrades/Mods/Fusers/CommonModFuser"); + modsAll.delete("/Lotus/Upgrades/Mods/Fusers/UncommonModFuser"); + modsAll.delete("/Lotus/Upgrades/Mods/Fusers/RareModFuser"); modsAll.delete("/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser"); revalidateAuthz().then(() => { @@ -3625,8 +3646,12 @@ function doImport() { data: JSON.stringify({ inventory: JSON.parse($("#import-inventory").val()) }) - }).then(function (anyKnownKey) { - toast(loc(anyKnownKey ? "code_succImport" : "code_nothingToDo")); + }).then(function (err) { + if (err) { + toast(err == "noKnownKey" ? loc("code_nothingToDo") : err); + } else { + toast(loc("code_succImport")); + } updateInventory(); }); } catch (e) { diff --git a/static/webui/translations/de.js b/static/webui/translations/de.js index ee0e7efe..faa06219 100644 --- a/static/webui/translations/de.js +++ b/static/webui/translations/de.js @@ -23,6 +23,9 @@ dict = { code_moteAmp: `Anfangsverstärker`, code_amp: `Verstärker`, code_kDrive: `K-Drive`, + code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`, + code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`, + code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`, code_legendaryCore: `Legendärer Kern`, code_traumaticPeculiar: `Kuriose Mod: Traumatisch`, code_starter: `|MOD| (Defekt)`, diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js index a83545f9..ecfa43da 100644 --- a/static/webui/translations/en.js +++ b/static/webui/translations/en.js @@ -22,6 +22,9 @@ dict = { code_moteAmp: `Mote Amp`, code_amp: `Amp`, code_kDrive: `K-Drive`, + code_fusionCoreCommon: `Fusion Core (Common)`, + code_fusionCoreUncommon: `Fusion Core (Uncommon)`, + code_fusionCoreRare: `Fusion Core (Rare)`, code_legendaryCore: `Legendary Core`, code_traumaticPeculiar: `Traumatic Peculiar`, code_starter: `|MOD| (Flawed)`, diff --git a/static/webui/translations/es.js b/static/webui/translations/es.js index 737ccbda..f63bdfb8 100644 --- a/static/webui/translations/es.js +++ b/static/webui/translations/es.js @@ -23,6 +23,9 @@ dict = { code_moteAmp: `Amp Mota`, code_amp: `Amp`, code_kDrive: `K-Drive`, + code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`, + code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`, + code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`, code_legendaryCore: `Núcleo legendario`, code_traumaticPeculiar: `Traumatismo peculiar`, code_starter: `|MOD| (Defectuoso)`, diff --git a/static/webui/translations/fr.js b/static/webui/translations/fr.js index a4ae4fca..ed0da195 100644 --- a/static/webui/translations/fr.js +++ b/static/webui/translations/fr.js @@ -23,6 +23,9 @@ dict = { code_moteAmp: `Amplificateur Faible`, code_amp: `Amplificateur`, code_kDrive: `K-Drive`, + code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`, + code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`, + code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`, code_legendaryCore: `Coeur Légendaire`, code_traumaticPeculiar: `Traumatisme Atypique`, code_starter: `|MOD| (Défectueux)`, diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js index 1f914223..6b36eca1 100644 --- a/static/webui/translations/ru.js +++ b/static/webui/translations/ru.js @@ -23,6 +23,9 @@ dict = { code_moteAmp: `Пылинка`, code_amp: `Усилитель`, code_kDrive: `К-Драйв`, + code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`, + code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`, + code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`, code_legendaryCore: `Легендарное ядро`, code_traumaticPeculiar: `Травмирующая Странность`, code_starter: `|MOD| (Повреждённый)`, diff --git a/static/webui/translations/uk.js b/static/webui/translations/uk.js index 18a8b011..7800920c 100644 --- a/static/webui/translations/uk.js +++ b/static/webui/translations/uk.js @@ -23,6 +23,9 @@ dict = { code_moteAmp: `Порошинка`, code_amp: `Підсилювач`, code_kDrive: `К-Драйв`, + code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`, + code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`, + code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`, code_legendaryCore: `Легендарне ядро`, code_traumaticPeculiar: `Особливе травмування`, code_starter: `|MOD| (Пошкоджений)`, diff --git a/static/webui/translations/zh.js b/static/webui/translations/zh.js index 795f6dbb..39a4412d 100644 --- a/static/webui/translations/zh.js +++ b/static/webui/translations/zh.js @@ -23,6 +23,9 @@ dict = { code_moteAmp: `微尘增幅器`, code_amp: `增幅器`, code_kDrive: `K式悬浮板`, + code_fusionCoreCommon: `[UNTRANSLATED] Fusion Core (Common)`, + code_fusionCoreUncommon: `[UNTRANSLATED] Fusion Core (Uncommon)`, + code_fusionCoreRare: `[UNTRANSLATED] Fusion Core (Rare)`, code_legendaryCore: `传奇核心`, code_traumaticPeculiar: `创伤怪奇`, code_starter: `|MOD| (有瑕疵的)`,