From 02f09357102fe0fa6c1432fa57ad2ea85d203e7c Mon Sep 17 00:00:00 2001 From: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Date: Thu, 9 Oct 2025 23:02:09 -0700 Subject: [PATCH] feat(webui): skins (#2816) Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2816 Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com> Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com> --- package-lock.json | 8 +- package.json | 2 +- src/controllers/api/inventoryController.ts | 2 +- src/controllers/api/sellController.ts | 9 ++ .../custom/getItemListsController.ts | 20 +++- .../getProfileViewingDataController.ts | 13 +-- src/helpers/skinLookupTable.ts | 8 ++ src/services/inventoryService.ts | 96 ++++++++++++++++++- static/webui/index.html | 20 ++++ static/webui/script.js | 78 +++++++++++++++ static/webui/translations/de.js | 6 ++ static/webui/translations/en.js | 6 ++ static/webui/translations/es.js | 6 ++ static/webui/translations/fr.js | 6 ++ static/webui/translations/ru.js | 6 ++ static/webui/translations/uk.js | 6 ++ static/webui/translations/zh.js | 6 ++ 17 files changed, 276 insertions(+), 22 deletions(-) create mode 100644 src/helpers/skinLookupTable.ts diff --git a/package-lock.json b/package-lock.json index b7ad3ae0..acb170d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "morgan": "^1.10.0", "ncp": "^2.0.0", "undici": "^7.10.0", - "warframe-public-export-plus": "^0.5.89", + "warframe-public-export-plus": "^0.5.90", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0", @@ -5534,9 +5534,9 @@ } }, "node_modules/warframe-public-export-plus": { - "version": "0.5.89", - "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.89.tgz", - "integrity": "sha512-a6dM1MirzofSsuv3LlRQHFLSSIGKPVSN93dcXSDmA3njsWqOGjJJdWyXqcyxxYw8rEB8CNowSHst/MUmKvKlRg==" + "version": "0.5.90", + "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.90.tgz", + "integrity": "sha512-PzYFJ+qOZawPOVx9+hGlOosGdqSJMSRL6c1z3CCXMiY5SEHUs7p9JIMjkqnvg1g7vflVWnyG6E5/2gYkQs0d7w==" }, "node_modules/warframe-riven-info": { "version": "0.1.2", diff --git a/package.json b/package.json index 3b8c2f1f..4509ff08 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "morgan": "^1.10.0", "ncp": "^2.0.0", "undici": "^7.10.0", - "warframe-public-export-plus": "^0.5.89", + "warframe-public-export-plus": "^0.5.90", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0", diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts index f5b2b2b3..4a426b32 100644 --- a/src/controllers/api/inventoryController.ts +++ b/src/controllers/api/inventoryController.ts @@ -177,7 +177,7 @@ export const inventoryController: RequestHandler = async (request, response) => } } - cleanupInventory(inventory); + await cleanupInventory(inventory); inventory.NextRefill = new Date((today + 1) * 86400000); // tomorrow at 0 UTC //await inventory.save(); diff --git a/src/controllers/api/sellController.ts b/src/controllers/api/sellController.ts index cf74ca92..b32d2aa4 100644 --- a/src/controllers/api/sellController.ts +++ b/src/controllers/api/sellController.ts @@ -77,6 +77,9 @@ export const sellController: RequestHandler = async (req, res) => { requiredFields.add("CrewShipSalvagedWeaponSkins"); } } + if (payload.Items.WeaponSkins) { + requiredFields.add("WeaponSkins"); + } const inventory = await getInventory(accountId, Array.from(requiredFields).join(" ")); // Give currency @@ -302,6 +305,11 @@ export const sellController: RequestHandler = async (req, res) => { addFusionTreasures(inventory, [parseFusionTreasure(sellItem.String, sellItem.Count * -1)]); }); } + if (payload.Items.WeaponSkins) { + payload.Items.WeaponSkins.forEach(sellItem => { + inventory.WeaponSkins.pull({ _id: sellItem.String }); + }); + } await inventory.save(); res.json({ @@ -335,6 +343,7 @@ interface ISellRequest { CrewShipWeapons?: ISellItem[]; CrewShipWeaponSkins?: ISellItem[]; FusionTreasures?: ISellItem[]; + WeaponSkins?: ISellItem[]; // SNS specific field }; SellPrice: number; SellCurrency: diff --git a/src/controllers/custom/getItemListsController.ts b/src/controllers/custom/getItemListsController.ts index 2aa3f120..3c2f9bd7 100644 --- a/src/controllers/custom/getItemListsController.ts +++ b/src/controllers/custom/getItemListsController.ts @@ -66,6 +66,7 @@ interface ItemLists { VaultDecoRecipes: ListedItem[]; FlavourItems: ListedItem[]; ShipDecorations: ListedItem[]; + WeaponSkins: ListedItem[]; //circuitGameModes: ListedItem[]; } @@ -107,7 +108,8 @@ const getItemListsController: RequestHandler = (req, response) => { TechProjects: [], VaultDecoRecipes: [], FlavourItems: [], - ShipDecorations: [] + ShipDecorations: [], + WeaponSkins: [] /*circuitGameModes: [ { uniqueName: "Survival", @@ -298,10 +300,18 @@ const getItemListsController: RequestHandler = (req, response) => { }); } for (const [uniqueName, item] of Object.entries(ExportCustoms)) { - res.miscitems.push({ - uniqueName: uniqueName, - name: getString(item.name, lang) - }); + if ( + item.productCategory == "WeaponSkins" && + !uniqueName.startsWith("/Lotus/Types/Game/Lotus") && // Base Items + !uniqueName.endsWith("ProjectileSkin") && // UnrealTournament ProjectileSkins + !uniqueName.endsWith("Coat") // Frost Prime stuff + ) { + res.WeaponSkins.push({ + uniqueName: uniqueName, + name: getString(item.name, lang), + alwaysAvailable: item.alwaysAvailable + }); + } } for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) { diff --git a/src/controllers/dynamic/getProfileViewingDataController.ts b/src/controllers/dynamic/getProfileViewingDataController.ts index 808c3242..40e57c58 100644 --- a/src/controllers/dynamic/getProfileViewingDataController.ts +++ b/src/controllers/dynamic/getProfileViewingDataController.ts @@ -20,13 +20,14 @@ import type { } from "../../types/inventoryTypes/inventoryTypes.ts"; import { LoadoutIndex } from "../../types/inventoryTypes/inventoryTypes.ts"; import type { RequestHandler } from "express"; -import { catBreadHash, getJSONfromString } from "../../helpers/stringHelpers.ts"; -import { ExportCustoms, ExportDojoRecipes } from "warframe-public-export-plus"; +import { getJSONfromString } from "../../helpers/stringHelpers.ts"; +import { ExportDojoRecipes } from "warframe-public-export-plus"; import type { IStatsClient } from "../../types/statTypes.ts"; import { toStoreItem } from "../../services/itemDataService.ts"; import type { FlattenMaps } from "mongoose"; import type { IEquipmentClient } from "../../types/equipmentTypes.ts"; import type { ILoadoutConfigClient } from "../../types/saveLoadoutTypes.ts"; +import { skinLookupTable } from "../../helpers/skinLookupTable.ts"; const getProfileViewingDataByPlayerIdImpl = async (playerId: string): Promise => { const account = await Account.findById(playerId, "DisplayName"); @@ -261,8 +262,6 @@ interface IXPComponentClient { locTags?: Record; } -let skinLookupTable: Record | undefined; - const resolveAndCollectSkins = ( inventory: TInventoryDatabaseDocument, skins: Set, @@ -274,12 +273,6 @@ const resolveAndCollectSkins = ( // Resolve oids to type names if (config.Skins[i].length == 24) { if (config.Skins[i].substring(0, 16) == "ca70ca70ca70ca70") { - if (!skinLookupTable) { - skinLookupTable = {}; - for (const key of Object.keys(ExportCustoms)) { - skinLookupTable[catBreadHash(key)] = key; - } - } config.Skins[i] = skinLookupTable[parseInt(config.Skins[i].substring(16), 16)]; } else { const skinItem = inventory.WeaponSkins.id(config.Skins[i]); diff --git a/src/helpers/skinLookupTable.ts b/src/helpers/skinLookupTable.ts new file mode 100644 index 00000000..24e7496f --- /dev/null +++ b/src/helpers/skinLookupTable.ts @@ -0,0 +1,8 @@ +import { ExportCustoms } from "warframe-public-export-plus"; +import { catBreadHash } from "./stringHelpers.ts"; + +export const skinLookupTable: Record = {}; + +for (const key of Object.keys(ExportCustoms)) { + skinLookupTable[catBreadHash(key)] = key; +} diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 37ab764d..f3b2159c 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -92,6 +92,7 @@ import type { } from "../types/equipmentTypes.ts"; import { EquipmentFeatures, Status } from "../types/equipmentTypes.ts"; import type { ITypeCount } from "../types/commonTypes.ts"; +import { skinLookupTable } from "../helpers/skinLookupTable.ts"; export const createInventory = async ( accountOwnerId: Types.ObjectId, @@ -2260,7 +2261,7 @@ export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void }); }; -export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void => { +export const cleanupInventory = async (inventory: TInventoryDatabaseDocument): Promise => { inventory.CurrentLoadOutIds = inventory.CurrentLoadOutIds.map(fromDbOid); let index = inventory.MiscItems.findIndex(x => x.ItemType == ""); @@ -2314,6 +2315,99 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void => logger.debug(`removed ModularParts from ${numFixed} non-modular items`); } } + + { + const weaponMap = new Map(); + for (const skin of inventory.WeaponSkins) { + weaponMap.set(skin.ItemType, skin._id.toString()); + } + + const itemsToAdd = new Set(); + + for (const key of equipmentKeys) { + if (key in inventory) { + for (const equipment of inventory[key]) { + for (const config of equipment.Configs) { + if (config.Skins) collectSkins(config.Skins, weaponMap, itemsToAdd); + } + } + } + } + + for (const key of ["AdultOperatorLoadOuts", "OperatorLoadOuts", "KahlLoadOuts"] as const) { + if (key in inventory) { + for (const loadOut of inventory[key]) { + if (loadOut.Skins) collectSkins(loadOut.Skins, weaponMap, itemsToAdd); + } + } + } + + if (inventory.LotusCustomization?.Skins) + collectSkins(inventory.LotusCustomization.Skins, weaponMap, itemsToAdd); + + if (itemsToAdd.size > 0) { + logger.debug(`Adding ${itemsToAdd.size} items due to migration from unlockAllSkins cheat`); + const inventoryChanges = await addItems(inventory, Array.from(itemsToAdd)); + + if (inventoryChanges.WeaponSkins) { + for (const skin of inventoryChanges.WeaponSkins as IWeaponSkinClient[]) { + weaponMap.set(skin.ItemType, skin.ItemId.toString()); + } + } + + for (const key of equipmentKeys) { + if (key in inventory) { + for (const equipment of inventory[key]) { + for (const config of equipment.Configs) { + if (config.Skins) replaceSkinIds(config.Skins, weaponMap); + } + } + } + } + + for (const key of ["AdultOperatorLoadOuts", "OperatorLoadOuts", "KahlLoadOuts"] as const) { + if (key in inventory) { + for (const loadOut of inventory[key]) { + if (loadOut.Skins) replaceSkinIds(loadOut.Skins, weaponMap); + } + } + } + + if (inventory.LotusCustomization?.Skins) replaceSkinIds(inventory.LotusCustomization.Skins, weaponMap); + } + } +}; + +const collectSkins = (skins: string[], weaponMap: Map, itemsToAdd: Set): void => { + for (const skinId of skins) { + if (skinId.startsWith("ca70ca70ca70ca70")) { + const typeName = skinLookupTable[parseInt(skinId.slice(16), 16)]; + if (!weaponMap.has(typeName)) { + const { requirement } = ExportCustoms[typeName]; + if (typeof requirement == "string") { + itemsToAdd.add(requirement); + } else { + itemsToAdd.add(typeName); + } + } + } + } +}; + +const replaceSkinIds = (skins: string[], weaponMap: Map): void => { + for (let i = 0; i < skins.length; i++) { + const skinId = skins[i]; + if (skinId.startsWith("ca70ca70ca70ca70")) { + const typeName = skinLookupTable[parseInt(skinId.slice(16), 16)]; + const inventoryId = weaponMap.get(typeName); + if (inventoryId) { + skins[i] = inventoryId; + } else if (typeName in ExportCustoms) { + const { requirement } = ExportCustoms[typeName]; + skins[i] = typeof requirement == "string" ? typeName : ""; + } + } + } }; export const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => { diff --git a/static/webui/index.html b/static/webui/index.html index 2e0dd64d..7a5c0caf 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -526,6 +526,24 @@ +
+
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+
@@ -538,6 +556,7 @@ +
@@ -1528,6 +1547,7 @@ + diff --git a/static/webui/script.js b/static/webui/script.js index 74a58c11..d4ecc107 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -599,6 +599,46 @@ function fetchItemList() { } itemMap[item.uniqueName] = { ...item, type }; }); + } else if (type == "WeaponSkins") { + let beardNumber = 1; + let cutNumber = 13; + let adultHeadNumber = 1; + let headNumber = 1; + items.forEach(item => { + if (item.name == "") { + if (item.uniqueName.includes("/Beards/")) { + item.name = loc("code_drifterBeardName") + .split("|INDEX|") + .join(beardNumber.toString().padStart(3, "0")); + beardNumber++; + } else if (item.uniqueName.includes("/Hair/")) { + item.name = loc("code_cutName") + .split("|INDEX|") + .join(cutNumber.toString().padStart(3, "0")); + cutNumber++; + if (cutNumber == 19) cutNumber = 21; + } else if (item.uniqueName.includes("/Heads/Adult")) { + item.name = loc("code_drifterFaceName") + .split("|INDEX|") + .join(adultHeadNumber.toString().padStart(3, "0")); + adultHeadNumber++; + } else if (item.uniqueName.includes("/Heads/")) { + item.name = loc("code_operatorFaceName") + .split("|INDEX|") + .join(headNumber.toString().padStart(3, "0")); + headNumber++; + } else { + item.name = item.uniqueName; + } + } + if (!item.alwaysAvailable) { + const option = document.createElement("option"); + option.setAttribute("data-key", item.uniqueName); + option.value = item.name; + document.getElementById("datalist-" + type).appendChild(option); + } + itemMap[item.uniqueName] = { ...item, type }; + }); } else { const nameToItems = {}; items.forEach(item => { @@ -1103,6 +1143,44 @@ function updateInventory() { document.getElementById("FlavourItems-list").appendChild(tr); }); + document.getElementById("WeaponSkins-list").innerHTML = ""; + data.WeaponSkins.forEach(item => { + if (item.ItemId.$oid.startsWith("ca70ca70ca70ca70")) return; + const datalist = document.getElementById("datalist-WeaponSkins"); + const optionToRemove = datalist.querySelector(`option[data-key="${item.ItemType}"]`); + if (optionToRemove) { + datalist.removeChild(optionToRemove); + } + const tr = document.createElement("tr"); + { + const td = document.createElement("td"); + const name = itemMap[item.ItemType]?.name?.trim(); + td.textContent = name || item.ItemType; + tr.appendChild(td); + } + { + const td = document.createElement("td"); + td.classList = "text-end text-nowrap"; + { + const a = document.createElement("a"); + a.href = "#"; + a.onclick = function (event) { + event.preventDefault(); + document.getElementById("WeaponSkins-list").removeChild(tr); + reAddToItemList(itemMap, "WeaponSkins", item.ItemType); + disposeOfGear("WeaponSkins", item.ItemId.$oid); + }; + a.title = loc("code_remove"); + a.innerHTML = ``; + td.appendChild(a); + } + + tr.appendChild(td); + } + + document.getElementById("WeaponSkins-list").appendChild(tr); + }); + const datalistEvolutionProgress = document.querySelectorAll("#datalist-EvolutionProgress option"); const formEvolutionProgress = document.querySelector('form[onsubmit*="doAcquireEvolution()"]'); diff --git a/static/webui/translations/de.js b/static/webui/translations/de.js index e0d1373f..2f428a50 100644 --- a/static/webui/translations/de.js +++ b/static/webui/translations/de.js @@ -75,6 +75,10 @@ dict = { code_funded: `[UNTRANSLATED] Funded`, code_replays: `[UNTRANSLATED] Replays`, code_stalker: `Stalker`, + code_cutName: `Frisur: |INDEX|`, + code_drifterBeardName: `Drifter-Bart: |INDEX|`, + code_drifterFaceName: `Drifter-Gesicht: |INDEX|`, + code_operatorFaceName: `Operator-Gesicht: |INDEX|`, code_succChange: `Erfolgreich geändert.`, code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`, login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`, @@ -112,6 +116,7 @@ dict = { inventory_boosters: `Booster`, inventory_flavourItems: `Sammlerstücke`, inventory_shipDecorations: `Schiffsdekorationen`, + inventory_weaponSkins: `Skins`, inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`, inventory_bulkAddWeapons: `Fehlende Waffen hinzufügen`, inventory_bulkAddSpaceSuits: `Fehlende Archwings hinzufügen`, @@ -121,6 +126,7 @@ dict = { inventory_bulkAddFlavourItems: `[UNTRANSLATED] Add Missing Flavour Items`, inventory_bulkAddShipDecorations: `[UNTRANSLATED] Add Missing Ship Decorations`, inventory_bulkAddEvolutionProgress: `Fehlende Incarnon-Entwicklungsfortschritte hinzufügen`, + inventory_bulkAddWeaponSkins: `[UNTRANSLATED] Add Missing Skins`, inventory_bulkRankUpSuits: `Alle Warframes auf Max. Rang`, inventory_bulkRankUpWeapons: `Alle Waffen auf Max. Rang`, inventory_bulkRankUpSpaceSuits: `Alle Archwings auf Max. Rang`, diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js index 8d43a83d..0c24676c 100644 --- a/static/webui/translations/en.js +++ b/static/webui/translations/en.js @@ -74,6 +74,10 @@ dict = { code_funded: `Funded`, code_replays: `Replays`, code_stalker: `Stalker`, + code_cutName: `Cut |INDEX|`, + code_drifterBeardName: `Drifter Beard |INDEX|`, + code_drifterFaceName: `Drifter Visage |INDEX|`, + code_operatorFaceName: `Operator Visage |INDEX|`, code_succChange: `Successfully changed.`, code_requiredInvigorationUpgrade: `You must select both an offensive & utility upgrade.`, login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`, @@ -111,6 +115,7 @@ dict = { inventory_boosters: `Boosters`, inventory_flavourItems: `Flavour Items`, inventory_shipDecorations: `Ship Decorations`, + inventory_weaponSkins: `Skins`, inventory_bulkAddSuits: `Add Missing Warframes`, inventory_bulkAddWeapons: `Add Missing Weapons`, inventory_bulkAddSpaceSuits: `Add Missing Archwings`, @@ -120,6 +125,7 @@ dict = { inventory_bulkAddFlavourItems: `Add Missing Flavour Items`, inventory_bulkAddShipDecorations: `Add Missing Ship Decorations`, inventory_bulkAddEvolutionProgress: `Add Missing Incarnon Evolution Progress`, + inventory_bulkAddWeaponSkins: `Add Missing Skins`, inventory_bulkRankUpSuits: `Max Rank All Warframes`, inventory_bulkRankUpWeapons: `Max Rank All Weapons`, inventory_bulkRankUpSpaceSuits: `Max Rank All Archwings`, diff --git a/static/webui/translations/es.js b/static/webui/translations/es.js index 05f841e2..7c6d2438 100644 --- a/static/webui/translations/es.js +++ b/static/webui/translations/es.js @@ -75,6 +75,10 @@ dict = { code_funded: `Financiado`, code_replays: `Repeticiones`, code_stalker: `Stalker`, + code_cutName: `[UNTRANSLATED] Cut |INDEX|`, + code_drifterBeardName: `Barba del Viajero: |INDEX|`, + code_drifterFaceName: `Rostro del Viajero |INDEX|`, + code_operatorFaceName: `Rostro del operador |INDEX|`, code_succChange: `Cambiado correctamente`, code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una mejora de utilidad.`, login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`, @@ -112,6 +116,7 @@ dict = { inventory_boosters: `Potenciadores`, inventory_flavourItems: `Ítems estéticos`, inventory_shipDecorations: `Decoraciones de nave`, + inventory_weaponSkins: `Diseños`, inventory_bulkAddSuits: `Agregar Warframes faltantes`, inventory_bulkAddWeapons: `Agregar armas faltantes`, inventory_bulkAddSpaceSuits: `Agregar Archwings faltantes`, @@ -121,6 +126,7 @@ dict = { inventory_bulkAddFlavourItems: `Añadir items estéticos faltantes`, inventory_bulkAddShipDecorations: `Añadir decoraciones de Nave Faltantes`, inventory_bulkAddEvolutionProgress: `Completar el progreso de evolución Incarnon faltante`, + inventory_bulkAddWeaponSkins: `[UNTRANSLATED] Add Missing Skins`, inventory_bulkRankUpSuits: `Maximizar rango de todos los Warframes`, inventory_bulkRankUpWeapons: `Maximizar rango de todas las armas`, inventory_bulkRankUpSpaceSuits: `Maximizar rango de todos los Archwings`, diff --git a/static/webui/translations/fr.js b/static/webui/translations/fr.js index 3992e64d..087a1afe 100644 --- a/static/webui/translations/fr.js +++ b/static/webui/translations/fr.js @@ -75,6 +75,10 @@ dict = { code_funded: `Complété`, code_replays: `[UNTRANSLATED] Replays`, code_stalker: `Stalker`, + code_cutName: `[UNTRANSLATED] Cut |INDEX|`, + code_drifterBeardName: `Barbe du Voyageur |INDEX|`, + code_drifterFaceName: `Visage du Voyageur |INDEX|`, + code_operatorFaceName: `Visage de l'Opérateur |INDEX|`, code_succChange: `Changement effectué.`, code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`, login_description: `Connexion avec les informations de connexion OpenWF.`, @@ -112,6 +116,7 @@ dict = { inventory_boosters: `Boosters`, inventory_flavourItems: `[UNTRANSLATED] Flavour Items`, inventory_shipDecorations: `Décorations du vaisseau`, + inventory_weaponSkins: `Aspects`, inventory_bulkAddSuits: `Ajouter les Warframes manquantes`, inventory_bulkAddWeapons: `Ajouter les armes manquantes`, inventory_bulkAddSpaceSuits: `Ajouter les Archwings manquants`, @@ -121,6 +126,7 @@ dict = { inventory_bulkAddFlavourItems: `[UNTRANSLATED] Add Missing Flavour Items`, inventory_bulkAddShipDecorations: `[UNTRANSLATED] Add Missing Ship Decorations`, inventory_bulkAddEvolutionProgress: `Ajouter les évolutions Incarnon manquantes`, + inventory_bulkAddWeaponSkins: `[UNTRANSLATED] Add Missing Skins`, inventory_bulkRankUpSuits: `Toutes les Warframes au rang max`, inventory_bulkRankUpWeapons: `Toutes les armes au rang max`, inventory_bulkRankUpSpaceSuits: `Tous les Archwings au rang max`, diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js index df92840f..0f66fa9b 100644 --- a/static/webui/translations/ru.js +++ b/static/webui/translations/ru.js @@ -75,6 +75,10 @@ dict = { code_funded: `Профинансировано`, code_replays: `Повторов`, code_stalker: `Сталкер`, + code_cutName: `Причёска: |INDEX|`, + code_drifterBeardName: `Борода скитальца: |INDEX|`, + code_drifterFaceName: `Внешность скитальца: |INDEX|`, + code_operatorFaceName: `Внешность оператора: |INDEX|`, code_succChange: `Успешно изменено.`, code_requiredInvigorationUpgrade: `Вы должны выбрать как атакующее, так и вспомогательное улучшение.`, login_description: `Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).`, @@ -112,6 +116,7 @@ dict = { inventory_boosters: `Бустеры`, inventory_flavourItems: `Уникальные предметы`, inventory_shipDecorations: `Украшения корабля`, + inventory_weaponSkins: `Скины`, inventory_bulkAddSuits: `Добавить отсутствующие Варфреймы`, inventory_bulkAddWeapons: `Добавить отсутствующее оружие`, inventory_bulkAddSpaceSuits: `Добавить отсутствующие Арчвинги`, @@ -121,6 +126,7 @@ dict = { inventory_bulkAddFlavourItems: `Добавить отсутствующие уникальные предметы`, inventory_bulkAddShipDecorations: `Добавить отсутствующие украшения корабля`, inventory_bulkAddEvolutionProgress: `Добавить отсутствующий прогресс эволюции Инкарнонов`, + inventory_bulkAddWeaponSkins: `Добавить отсутствующие скины`, inventory_bulkRankUpSuits: `Макс. ранг всех Варфреймов`, inventory_bulkRankUpWeapons: `Макс. ранг всего оружия`, inventory_bulkRankUpSpaceSuits: `Макс. ранг всех Арчвингов`, diff --git a/static/webui/translations/uk.js b/static/webui/translations/uk.js index d89e541a..50680791 100644 --- a/static/webui/translations/uk.js +++ b/static/webui/translations/uk.js @@ -75,6 +75,10 @@ dict = { code_funded: `Профінансовано`, code_replays: `Повтори`, code_stalker: `Сталкер`, + code_cutName: `Зачіска: |INDEX|`, + code_drifterBeardName: `Борода мандрівника: |INDEX|`, + code_drifterFaceName: `Зовнішність мандрівника: |INDEX|`, + code_operatorFaceName: `Зовнішність оператора: |INDEX|`, code_succChange: `Успішно змінено.`, code_requiredInvigorationUpgrade: `Ви повинні вибрати як атакуюче, так і допоміжне вдосконалення.`, login_description: `Увійдіть, використовуючи облікові дані OpenWF (ті ж, що й у грі при підключенні до цього серверу).`, @@ -112,6 +116,7 @@ dict = { inventory_boosters: `Посилення`, inventory_flavourItems: `Унікальні предмети`, inventory_shipDecorations: `Прикраси судна`, + inventory_weaponSkins: `Вигляди`, inventory_bulkAddSuits: `Додати відсутні Ворфрейми`, inventory_bulkAddWeapons: `Додати відсутню зброю`, inventory_bulkAddSpaceSuits: `Додати відсутні Арквінґи`, @@ -121,6 +126,7 @@ dict = { inventory_bulkAddFlavourItems: `Додати відсутні унікальні предмети`, inventory_bulkAddShipDecorations: `Додати відсутні оздоби корабля`, inventory_bulkAddEvolutionProgress: `Додати відсутній прогрес еволюції Інкарнонів`, + inventory_bulkAddWeaponSkins: `[UNTRANSLATED] Add Missing Skins`, inventory_bulkRankUpSuits: `Макс. рівень всіх Ворфреймів`, inventory_bulkRankUpWeapons: `Макс. рівень всієї зброї`, inventory_bulkRankUpSpaceSuits: `Макс. рівень всіх Арквінґів`, diff --git a/static/webui/translations/zh.js b/static/webui/translations/zh.js index dac92db9..573f26e2 100644 --- a/static/webui/translations/zh.js +++ b/static/webui/translations/zh.js @@ -75,6 +75,10 @@ dict = { code_funded: `[UNTRANSLATED] Funded`, code_replays: `[UNTRANSLATED] Replays`, code_stalker: `追猎者`, + code_cutName: `[UNTRANSLATED] Cut |INDEX|`, + code_drifterBeardName: `漂泊者胡须 |INDEX|`, + code_drifterFaceName: `漂泊者面部 |INDEX|`, + code_operatorFaceName: `指挥官面部 |INDEX|`, code_succChange: `更改成功`, code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`, login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)`, @@ -112,6 +116,7 @@ dict = { inventory_boosters: `加成器`, inventory_flavourItems: `装饰物品`, inventory_shipDecorations: `飞船装饰`, + inventory_weaponSkins: `外观`, inventory_bulkAddSuits: `添加缺失战甲`, inventory_bulkAddWeapons: `添加缺失武器`, inventory_bulkAddSpaceSuits: `添加缺失载具`, @@ -121,6 +126,7 @@ dict = { inventory_bulkAddFlavourItems: `[UNTRANSLATED] Add Missing Flavour Items`, inventory_bulkAddShipDecorations: `[UNTRANSLATED] Add Missing Ship Decorations`, inventory_bulkAddEvolutionProgress: `添加缺失的灵化之源进度`, + inventory_bulkAddWeaponSkins: `[UNTRANSLATED] Add Missing Skins`, inventory_bulkRankUpSuits: `所有战甲升满级`, inventory_bulkRankUpWeapons: `所有武器升满级`, inventory_bulkRankUpSpaceSuits: `所有载具升满级`,