From a9359bd989cfb7a25ceb5b625b45efffd08e04f8 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Fri, 27 Jun 2025 20:28:44 -0700 Subject: [PATCH] feat(webui): the circuit override (#2335) Re #2312 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2335 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com> --- README.md | 2 +- .../custom/getItemListsController.ts | 35 +++++++++++++ static/webui/index.html | 25 ++++++--- static/webui/script.js | 52 +++++++++++++++++-- static/webui/translations/de.js | 1 + static/webui/translations/en.js | 1 + static/webui/translations/es.js | 1 + static/webui/translations/fr.js | 1 + static/webui/translations/ru.js | 1 + static/webui/translations/zh.js | 1 + 10 files changed, 109 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5b2425ef..0985b846 100644 --- a/README.md +++ b/README.md @@ -35,4 +35,4 @@ SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [confi - `RadioLegionIntermissionSyndicate` for Intermission I - `RadioLegionSyndicate` for The Wolf of Saturn Six - `allTheFissures` can be set to `normal` or `hard` to enable all fissures either in normal or steel path, respectively. -- `worldState.circuitGameModes` can be provided with an array of valid game modes (`Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, `Alchemy`) +- `worldState.circuitGameModes` can be set to an array of game modes which will override the otherwise-random pattern in The Circuit. Valid element values are `Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, and `Alchemy`. diff --git a/src/controllers/custom/getItemListsController.ts b/src/controllers/custom/getItemListsController.ts index f804f81c..95f85b01 100644 --- a/src/controllers/custom/getItemListsController.ts +++ b/src/controllers/custom/getItemListsController.ts @@ -55,6 +55,7 @@ interface ItemLists { EvolutionProgress: ListedItem[]; mods: ListedItem[]; Boosters: ListedItem[]; + //circuitGameModes: ListedItem[]; } const relicQualitySuffixes: Record = { @@ -64,6 +65,10 @@ const relicQualitySuffixes: Record = { VPQ_PLATINUM: " [Exceptional]" }; +/*const toTitleCase = (str: string): string => { + return str.replace(/[^\s-]+/g, word => word.charAt(0).toUpperCase() + word.substr(1).toLowerCase()); +};*/ + const getItemListsController: RequestHandler = (req, response) => { const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en"); const res: ItemLists = { @@ -87,6 +92,36 @@ const getItemListsController: RequestHandler = (req, response) => { EvolutionProgress: [], mods: [], Boosters: [] + /*circuitGameModes: [ + { + uniqueName: "Survival", + name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Survival", lang)) + }, + { + uniqueName: "VoidFlood", + name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Corruption", lang)) + }, + { + uniqueName: "Excavation", + name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Excavation", lang)) + }, + { + uniqueName: "Defense", + name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Defense", lang)) + }, + { + uniqueName: "Exterminate", + name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Exterminate", lang)) + }, + { + uniqueName: "Assassination", + name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Assassination", lang)) + }, + { + uniqueName: "Alchemy", + name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Alchemy", lang)) + } + ]*/ }; for (const [uniqueName, item] of Object.entries(ExportWarframes)) { res[item.productCategory].push({ diff --git a/static/webui/index.html b/static/webui/index.html index 455ff556..425269b6 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -768,14 +768,14 @@ -
+
-
+
@@ -892,6 +892,13 @@
+ + +
+ + +
+
@@ -923,10 +930,7 @@ - - - - + @@ -958,6 +962,15 @@ + + + + + + + + + diff --git a/static/webui/script.js b/static/webui/script.js index 10c69dff..e6ba4989 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -1882,12 +1882,27 @@ for (const id of uiConfigs) { } } -function doSaveConfig(id) { - const elm = document.getElementById(id); +function doSaveConfigInt(id) { $.post({ url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid, contentType: "application/json", - data: JSON.stringify({ [id]: parseInt(elm.value) }) + data: JSON.stringify({ + [id]: parseInt(document.getElementById(id).value) + }) + }); +} + +function doSaveConfigStringArray(id) { + $.post({ + url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid, + contentType: "application/json", + data: JSON.stringify({ + [id]: document + .getElementById(id) + .getAttribute("data-tags-value") + .split(", ") + .filter(x => x) + }) }); } @@ -1914,7 +1929,12 @@ single.getRoute("/webui/cheats").on("beforeload", function () { if (x.type == "checkbox") { x.checked = value; } else if (x.type == "number") { - x.setAttribute("value", `${value}`); + x.setAttribute("value", value); + } else if (x.classList.contains("tags-input")) { + x.value = value.join(", "); + x.oninput(); + } else { + x.value = value; } } }); @@ -2597,3 +2617,27 @@ const importSamples = { function setImportSample(key) { $("#import-inventory").val(JSON.stringify(importSamples[key], null, 2)); } + +document.querySelectorAll(".tags-input").forEach(input => { + const datalist = document.getElementById(input.getAttribute("list")); + const options = [...datalist.querySelectorAll("option")].map(x => x.textContent); + input.oninput = function () { + const value = []; + for (const tag of this.value.split(",")) { + const index = options.map(x => x.toLowerCase()).indexOf(tag.trim().toLowerCase()); + if (index != -1) { + value.push(options[index]); + } + } + + this.setAttribute("data-tags-value", value.join(", ")); + + datalist.innerHTML = ""; + for (const option of options) { + const elm = document.createElement("option"); + elm.textContent = [...value, option, ""].join(", "); + datalist.appendChild(elm); + } + }; + input.oninput(); +}); diff --git a/static/webui/translations/de.js b/static/webui/translations/de.js index 22148c9d..6da6847d 100644 --- a/static/webui/translations/de.js +++ b/static/webui/translations/de.js @@ -230,6 +230,7 @@ dict = { normal: `[UNTRANSLATED] Normal`, worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`, worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`, + worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, import_importNote: `Du kannst hier eine vollständige oder teilweise Inventarantwort (Client-Darstellung) einfügen. Alle Felder, die vom Importer unterstützt werden, werden in deinem Account überschrieben.`, import_submit: `Absenden`, diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js index 22481df6..31631cde 100644 --- a/static/webui/translations/en.js +++ b/static/webui/translations/en.js @@ -229,6 +229,7 @@ dict = { normal: `Normal`, worldState_allAtOnceNormal: `All At Once, Normal`, worldState_allAtOnceSteelPath: `All At Once, Steel Path`, + worldState_theCircuitOverride: `The Circuit Override`, import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer will be overwritten in your account.`, import_submit: `Submit`, diff --git a/static/webui/translations/es.js b/static/webui/translations/es.js index 0a7b7ac5..85a4e0ad 100644 --- a/static/webui/translations/es.js +++ b/static/webui/translations/es.js @@ -230,6 +230,7 @@ dict = { normal: `[UNTRANSLATED] Normal`, worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`, worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`, + worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, import_importNote: `Puedes proporcionar una respuesta de inventario completa o parcial (representación del cliente) aquí. Todos los campos compatibles con el importador serán sobrescritos en tu cuenta.`, import_submit: `Enviar`, diff --git a/static/webui/translations/fr.js b/static/webui/translations/fr.js index 9dca3dbe..000a9387 100644 --- a/static/webui/translations/fr.js +++ b/static/webui/translations/fr.js @@ -230,6 +230,7 @@ dict = { normal: `[UNTRANSLATED] Normal`, worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`, worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`, + worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, import_importNote: `Import manuel. Toutes les modifcations supportées par l'inventaire écraseront celles présentes dans la base de données.`, import_submit: `Soumettre`, diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js index b276c0fb..ba727562 100644 --- a/static/webui/translations/ru.js +++ b/static/webui/translations/ru.js @@ -230,6 +230,7 @@ dict = { normal: `[UNTRANSLATED] Normal`, worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`, worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`, + worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля будут перезаписаны в вашем аккаунте.`, import_submit: `Отправить`, diff --git a/static/webui/translations/zh.js b/static/webui/translations/zh.js index e6973c34..9a37d110 100644 --- a/static/webui/translations/zh.js +++ b/static/webui/translations/zh.js @@ -230,6 +230,7 @@ dict = { normal: `正常`, worldState_allAtOnceNormal: `全部开启(普通)`, worldState_allAtOnceSteelPath: `全部开启(钢铁之路)`, + worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`, import_importNote: `您可以在此处提供完整或部分库存响应(客户端表示)。支持的所有字段将被覆盖到您的账户中。`, import_submit: `提交`,