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 8c88a69a..422881c5 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%技能范围`,