diff --git a/src/controllers/api/gildWeaponController.ts b/src/controllers/api/gildWeaponController.ts index dea62765..b9133426 100644 --- a/src/controllers/api/gildWeaponController.ts +++ b/src/controllers/api/gildWeaponController.ts @@ -34,10 +34,9 @@ export const gildWeaponController: RequestHandler = async (req, res) => { const weapon = inventory[data.Category][weaponIndex]; weapon.Features ??= 0; weapon.Features |= EquipmentFeatures.GILDED; - if (data.Recipe != "webui") { - weapon.ItemName = data.ItemName; - weapon.XP = 0; - } + weapon.ItemName = data.ItemName; + weapon.XP = 0; + if (data.Category != "OperatorAmps" && data.PolarizeSlot && data.PolarizeValue) { weapon.Polarity = [ { @@ -52,22 +51,20 @@ export const gildWeaponController: RequestHandler = async (req, res) => { const affiliationMods = []; - if (data.Recipe != "webui") { - const recipe = ExportRecipes[data.Recipe]; - inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({ - ItemType: ingredient.ItemType, - ItemCount: ingredient.ItemCount * -1 - })); - addMiscItems(inventory, inventoryChanges.MiscItems); + const recipe = ExportRecipes[data.Recipe]; + inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({ + ItemType: ingredient.ItemType, + ItemCount: ingredient.ItemCount * -1 + })); + addMiscItems(inventory, inventoryChanges.MiscItems); - if (recipe.syndicateStandingChange) { - const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!; - affiliation.Standing += recipe.syndicateStandingChange.value; - affiliationMods.push({ - Tag: recipe.syndicateStandingChange.tag, - Standing: recipe.syndicateStandingChange.value - }); - } + if (recipe.syndicateStandingChange) { + const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!; + affiliation.Standing += recipe.syndicateStandingChange.value; + affiliationMods.push({ + Tag: recipe.syndicateStandingChange.tag, + Standing: recipe.syndicateStandingChange.value + }); } await inventory.save(); diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts index 5d107510..b832c41b 100644 --- a/src/controllers/api/inventoryController.ts +++ b/src/controllers/api/inventoryController.ts @@ -410,6 +410,7 @@ export const getInventoryResponse = async ( for (const equipment of inventoryResponse[key]) { equipment.Features ??= 0; equipment.Features |= EquipmentFeatures.ARCANE_SLOT; + equipment.Features |= EquipmentFeatures.SECOND_ARCANE_SLOT; } } } diff --git a/src/controllers/custom/equipmentFeaturesController.ts b/src/controllers/custom/equipmentFeaturesController.ts new file mode 100644 index 00000000..68d0e60f --- /dev/null +++ b/src/controllers/custom/equipmentFeaturesController.ts @@ -0,0 +1,34 @@ +import type { RequestHandler } from "express"; +import { getAccountIdForRequest } from "../../services/loginService.ts"; +import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts"; +import { getInventory } from "../../services/inventoryService.ts"; +import { EquipmentFeatures } from "../../types/equipmentTypes.ts"; +import { sendWsBroadcastTo } from "../../services/wsService.ts"; + +export const equipmentFeaturesController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const category = req.query.Category as TEquipmentKey; + const inventory = await getInventory( + accountId, + `${category} unlockDoubleCapacityPotatoesEverywhere unlockExilusEverywhere unlockArcanesEverywhere` + ); + const bit = Number(req.query.bit) as EquipmentFeatures; + if ( + (inventory.unlockDoubleCapacityPotatoesEverywhere && bit === EquipmentFeatures.DOUBLE_CAPACITY) || + (inventory.unlockExilusEverywhere && bit === EquipmentFeatures.UTILITY_SLOT) || + (inventory.unlockArcanesEverywhere && + (bit === EquipmentFeatures.ARCANE_SLOT || bit === EquipmentFeatures.SECOND_ARCANE_SLOT)) + ) { + res.status(400).end(); + } + const item = inventory[category].id(req.query.ItemId as string); + if (item) { + item.Features ??= 0; + item.Features ^= bit; + await inventory.save(); + sendWsBroadcastTo(accountId, { sync_inventory: true }); + res.status(200).end(); + } else { + res.status(400).end(); + } +}; diff --git a/src/routes/custom.ts b/src/routes/custom.ts index 05747f5a..1a6fca1f 100644 --- a/src/routes/custom.ts +++ b/src/routes/custom.ts @@ -1,6 +1,7 @@ import express from "express"; import { tunablesController } from "../controllers/custom/tunablesController.ts"; +import { equipmentFeaturesController } from "../controllers/custom/equipmentFeaturesController.ts"; import { getItemListsController } from "../controllers/custom/getItemListsController.ts"; import { pushArchonCrystalUpgradeController } from "../controllers/custom/pushArchonCrystalUpgradeController.ts"; import { popArchonCrystalUpgradeController } from "../controllers/custom/popArchonCrystalUpgradeController.ts"; @@ -53,6 +54,7 @@ import { getConfigController, setConfigController } from "../controllers/custom/ const customRouter = express.Router(); customRouter.get("/tunables.json", tunablesController); +customRouter.get("/equipmentFeatures", equipmentFeaturesController); customRouter.get("/getItemLists", getItemListsController); customRouter.get("/pushArchonCrystalUpgrade", pushArchonCrystalUpgradeController); customRouter.get("/popArchonCrystalUpgrade", popArchonCrystalUpgradeController); diff --git a/static/webui/index.html b/static/webui/index.html index e9c3bcf3..75fa3d52 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -840,6 +840,10 @@ +
+
+
+

diff --git a/static/webui/script.js b/static/webui/script.js index 200bc040..4783a2e7 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -912,12 +912,7 @@ function updateInventory() { td.appendChild(a); } - if ( - ["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes( - category - ) || - modularWeapons.includes(item.ItemType) - ) { + { const a = document.createElement("a"); a.href = "/webui/detailedView?productCategory=" + category + "&itemId=" + item.ItemId.$oid; @@ -930,7 +925,7 @@ function updateInventory() { a.href = "#"; a.onclick = function (event) { event.preventDefault(); - gildEquipment(category, item.ItemId.$oid); + equipmentFeatures(category, item.ItemId.$oid, 8); }; a.title = loc("code_gild"); a.innerHTML = ``; @@ -1560,6 +1555,57 @@ function updateInventory() { $("#detailedView-title").text(itemName); } + { + document.getElementById("equipmentFeatures-card").classList.remove("d-none"); + const buttonsCard = document.getElementById("equipmentFeaturesButtons-card"); + buttonsCard.innerHTML = ""; + item.Features ??= 0; + const bits = []; + if (category != "OperatorAmps") bits.push(1); + if (["Suits", "LongGuns", "Pistols", "Melee"].includes(category)) bits.push(2); + if (modularWeapons.includes(item.ItemType)) bits.push(8); + if (["LongGuns", "Pistols", "Melee", "SpaceGuns", "OperatorAmps"].includes(category)) + bits.push(32); + if (category == "SpaceGuns") bits.push(4, 64); + if ( + ["LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category) && + item.UpgradeFingerprint + ) + bits.push(1024); + for (const bit of bits.sort((a, b) => a - b)) { + const wrapper = document.createElement("div"); + wrapper.classList = "form-check"; + + const input = document.createElement("input"); + input.classList = "form-check-input"; + input.type = "checkbox"; + input.id = `detailedView-feature-${bit}`; + input.checked = item.Features & bit; + + const label = document.createElement("label"); + label.classList = "form-check-label"; + label.htmlFor = input.id; + label.innerHTML = loc(`code_feature_${bit}`); + label.setAttribute("data-loc", `code_feature_${bit}`); + + input.onchange = function (event) { + event.preventDefault(); + equipmentFeatures(category, oid, bit); + }; + if ( + (data.unlockDoubleCapacityPotatoesEverywhere && bit === 1) || + (data.unlockExilusEverywhere && bit === 2) || + (data.unlockArcanesEverywhere && (bit === 32 || bit === 64)) + ) { + input.disabled = true; + } + + wrapper.appendChild(input); + wrapper.appendChild(label); + buttonsCard.appendChild(wrapper); + } + } + if (category == "Suits") { document.getElementById("archonShards-card").classList.remove("d-none"); @@ -2864,15 +2910,11 @@ function disposeOfItems(category, type, count) { }); } -function gildEquipment(category, oid) { +function equipmentFeatures(category, oid, bit) { revalidateAuthz().then(() => { - $.post({ - url: "/api/gildWeapon.php?" + window.authz + "&ItemId=" + oid + "&Category=" + category, - contentType: "application/octet-stream", - data: JSON.stringify({ - Recipe: "webui" - }) - }).done(function () { + $.get( + "/custom/equipmentFeatures?" + window.authz + "&ItemId=" + oid + "&Category=" + category + "&bit=" + bit + ).done(function () { updateInventory(); }); }); @@ -3478,6 +3520,8 @@ single.getRoute("#detailedView-route").on("beforeload", function () { document.getElementById("modularParts-card").classList.add("d-none"); document.getElementById("modularParts-form").innerHTML = ""; document.getElementById("valenceBonus-card").classList.add("d-none"); + document.getElementById("equipmentFeatures-card").classList.add("d-none"); + document.getElementById("equipmentFeaturesButtons-card").innerHTML = ""; if (window.didInitialInventoryUpdate) { updateInventory(); } diff --git a/static/webui/translations/de.js b/static/webui/translations/de.js index e47aa556..3bef4a97 100644 --- a/static/webui/translations/de.js +++ b/static/webui/translations/de.js @@ -81,6 +81,13 @@ dict = { code_operatorFaceName: `Operator-Gesicht: |INDEX|`, code_succChange: `Erfolgreich geändert.`, code_requiredInvigorationUpgrade: `Du musst sowohl ein Offensiv- als auch ein Support-Upgrade auswählen.`, + code_feature_1: `[UNTRANSLATED] Orokin Reactor`, + code_feature_2: `[UNTRANSLATED] Exilus Adapter`, + code_feature_4: `[UNTRANSLATED] Gravimag`, + code_feature_8: `[UNTRANSLATED] Gild`, + code_feature_32: `[UNTRANSLATED] Arcane Slot`, + code_feature_64: `[UNTRANSLATED] Second Arcane Slot`, + code_feature_1024: `[UNTRANSLATED] Valence Override`, login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`, login_emailLabel: `E-Mail-Adresse`, login_passwordLabel: `Passwort`, @@ -155,6 +162,7 @@ dict = { detailedView_modularPartsLabel: `Modulare Teile ändern`, detailedView_invigorationLabel: `Kräftigung`, detailedView_loadoutLabel: `Loadouts`, + detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`, invigorations_offensive_AbilityStrength: `+200% Fähigkeitsstärke`, invigorations_offensive_AbilityRange: `+100% Fähigkeitsreichweite`, diff --git a/static/webui/translations/en.js b/static/webui/translations/en.js index c9398726..75d3cd0c 100644 --- a/static/webui/translations/en.js +++ b/static/webui/translations/en.js @@ -80,6 +80,13 @@ dict = { code_operatorFaceName: `Operator Visage |INDEX|`, code_succChange: `Successfully changed.`, code_requiredInvigorationUpgrade: `You must select both an offensive & utility upgrade.`, + code_feature_1: `Orokin Reactor`, + code_feature_2: `Exilus Adapter`, + code_feature_4: `Gravimag`, + code_feature_8: `Gild`, + code_feature_32: `Arcane Slot`, + code_feature_64: `Second Arcane Slot`, + code_feature_1024: `Valence Override`, login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`, login_emailLabel: `Email address`, login_passwordLabel: `Password`, @@ -154,6 +161,7 @@ dict = { detailedView_modularPartsLabel: `Change Modular Parts`, detailedView_invigorationLabel: `Invigoration`, detailedView_loadoutLabel: `Loadouts`, + detailedView_equipmentFeaturesLabel: `Equipment Features`, invigorations_offensive_AbilityStrength: `+200% Ability Strength`, invigorations_offensive_AbilityRange: `+100% Ability Range`, diff --git a/static/webui/translations/es.js b/static/webui/translations/es.js index 93e576a0..5de24c77 100644 --- a/static/webui/translations/es.js +++ b/static/webui/translations/es.js @@ -81,6 +81,13 @@ dict = { code_operatorFaceName: `Rostro del operador |INDEX|`, code_succChange: `Cambiado correctamente`, code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una mejora de utilidad.`, + code_feature_1: `[UNTRANSLATED] Orokin Reactor`, + code_feature_2: `[UNTRANSLATED] Exilus Adapter`, + code_feature_4: `[UNTRANSLATED] Gravimag`, + code_feature_8: `[UNTRANSLATED] Gild`, + code_feature_32: `[UNTRANSLATED] Arcane Slot`, + code_feature_64: `[UNTRANSLATED] Second Arcane Slot`, + code_feature_1024: `[UNTRANSLATED] Valence Override`, login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`, login_emailLabel: `Dirección de correo electrónico`, login_passwordLabel: `Contraseña`, @@ -155,6 +162,7 @@ dict = { detailedView_modularPartsLabel: `Cambiar partes modulares`, detailedView_invigorationLabel: `Fortalecimiento`, detailedView_loadoutLabel: `Equipamientos`, + detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`, invigorations_offensive_AbilityStrength: `+200% Fuerza de Habilidad`, invigorations_offensive_AbilityRange: `+100% Alcance de Habilidad`, diff --git a/static/webui/translations/fr.js b/static/webui/translations/fr.js index 9bc24f9c..5246d35d 100644 --- a/static/webui/translations/fr.js +++ b/static/webui/translations/fr.js @@ -81,6 +81,13 @@ dict = { code_operatorFaceName: `Visage de l'Opérateur |INDEX|`, code_succChange: `Changement effectué.`, code_requiredInvigorationUpgrade: `Invigoration offensive et défensive requises.`, + code_feature_1: `[UNTRANSLATED] Orokin Reactor`, + code_feature_2: `[UNTRANSLATED] Exilus Adapter`, + code_feature_4: `[UNTRANSLATED] Gravimag`, + code_feature_8: `[UNTRANSLATED] Gild`, + code_feature_32: `[UNTRANSLATED] Arcane Slot`, + code_feature_64: `[UNTRANSLATED] Second Arcane Slot`, + code_feature_1024: `[UNTRANSLATED] Valence Override`, login_description: `Connexion avec les informations de connexion OpenWF.`, login_emailLabel: `Email`, login_passwordLabel: `Mot de passe`, @@ -155,6 +162,7 @@ dict = { detailedView_modularPartsLabel: `Changer l'équipement modulaire`, detailedView_invigorationLabel: `Dynamisation`, detailedView_loadoutLabel: `Équipements`, + detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`, invigorations_offensive_AbilityStrength: `+200% de puissance de pouvoir`, invigorations_offensive_AbilityRange: `+100% de portée de pouvoir`, diff --git a/static/webui/translations/ru.js b/static/webui/translations/ru.js index 4b3ad9a3..d9062c26 100644 --- a/static/webui/translations/ru.js +++ b/static/webui/translations/ru.js @@ -81,6 +81,13 @@ dict = { code_operatorFaceName: `Внешность оператора: |INDEX|`, code_succChange: `Успешно изменено.`, code_requiredInvigorationUpgrade: `Вы должны выбрать как атакующее, так и вспомогательное улучшение.`, + code_feature_1: `Реактор Орокин`, + code_feature_2: `Адаптер Эксилус`, + code_feature_4: `Гравимаг`, + code_feature_8: `Улучшение`, + code_feature_32: `Слот Мистификатора`, + code_feature_64: `Второй слот Мистификатора`, + code_feature_1024: `Переопределение валентности`, login_description: `Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).`, login_emailLabel: `Адрес электронной почты`, login_passwordLabel: `Пароль`, @@ -155,6 +162,7 @@ dict = { detailedView_modularPartsLabel: `Изменить модульные части`, detailedView_invigorationLabel: `Воодушевление`, detailedView_loadoutLabel: `Конфигурации`, + detailedView_equipmentFeaturesLabel: `Модификаторы снаряжения`, invigorations_offensive_AbilityStrength: `+200% к силе способностей.`, invigorations_offensive_AbilityRange: `+100% к зоне поражения способностей.`, diff --git a/static/webui/translations/uk.js b/static/webui/translations/uk.js index cdef5c70..422a9e36 100644 --- a/static/webui/translations/uk.js +++ b/static/webui/translations/uk.js @@ -81,6 +81,13 @@ dict = { code_operatorFaceName: `Зовнішність оператора: |INDEX|`, code_succChange: `Успішно змінено.`, code_requiredInvigorationUpgrade: `Ви повинні вибрати як атакуюче, так і допоміжне вдосконалення.`, + code_feature_1: `[UNTRANSLATED] Orokin Reactor`, + code_feature_2: `[UNTRANSLATED] Exilus Adapter`, + code_feature_4: `[UNTRANSLATED] Gravimag`, + code_feature_8: `[UNTRANSLATED] Gild`, + code_feature_32: `[UNTRANSLATED] Arcane Slot`, + code_feature_64: `[UNTRANSLATED] Second Arcane Slot`, + code_feature_1024: `[UNTRANSLATED] Valence Override`, login_description: `Увійдіть, використовуючи облікові дані OpenWF (ті ж, що й у грі при підключенні до цього серверу).`, login_emailLabel: `Адреса електронної пошти`, login_passwordLabel: `Пароль`, @@ -155,6 +162,7 @@ dict = { detailedView_modularPartsLabel: `Змінити модульні частини`, detailedView_invigorationLabel: `Зміцнення`, detailedView_loadoutLabel: `Конфігурації`, + detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`, invigorations_offensive_AbilityStrength: `+200% до потужності здібностей.`, invigorations_offensive_AbilityRange: `+100% до досяжності здібностей.`, diff --git a/static/webui/translations/zh.js b/static/webui/translations/zh.js index 58d8d467..b07c44a5 100644 --- a/static/webui/translations/zh.js +++ b/static/webui/translations/zh.js @@ -81,6 +81,13 @@ dict = { code_operatorFaceName: `指挥官面部 |INDEX|`, code_succChange: `更改成功`, code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`, + code_feature_1: `[UNTRANSLATED] Orokin Reactor`, + code_feature_2: `[UNTRANSLATED] Exilus Adapter`, + code_feature_4: `[UNTRANSLATED] Gravimag`, + code_feature_8: `[UNTRANSLATED] Gild`, + code_feature_32: `[UNTRANSLATED] Arcane Slot`, + code_feature_64: `[UNTRANSLATED] Second Arcane Slot`, + code_feature_1024: `[UNTRANSLATED] Valence Override`, login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)`, login_emailLabel: `电子邮箱`, login_passwordLabel: `密码`, @@ -155,6 +162,7 @@ dict = { detailedView_modularPartsLabel: `更换部件`, detailedView_invigorationLabel: `活化`, detailedView_loadoutLabel: `配置`, + detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`, invigorations_offensive_AbilityStrength: `+200%技能强度`, invigorations_offensive_AbilityRange: `+100%技能范围`,