XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

feat(webui): equipment features in detailed view (#2987)

Closes #2927 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2987 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>

98620169
AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
提交于

代码差异

13 个文件 +172 -34
Modified src/controllers/api/gildWeaponController.ts +16 -19
@@ -34,10 +34,9 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
34 34 const weapon = inventory[data.Category][weaponIndex];
35 35 weapon.Features ??= 0;
36 36 weapon.Features |= EquipmentFeatures.GILDED;
37 if (data.Recipe != "webui") {
38 weapon.ItemName = data.ItemName;
39 weapon.XP = 0;
40 }
37 weapon.ItemName = data.ItemName;
38 weapon.XP = 0;
39
41 40 if (data.Category != "OperatorAmps" && data.PolarizeSlot && data.PolarizeValue) {
42 41 weapon.Polarity = [
43 42 {
@@ -52,22 +51,20 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
52 51
53 52 const affiliationMods = [];
54 53
55 if (data.Recipe != "webui") {
56 const recipe = ExportRecipes[data.Recipe];
57 inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({
58 ItemType: ingredient.ItemType,
59 ItemCount: ingredient.ItemCount * -1
60 }));
61 addMiscItems(inventory, inventoryChanges.MiscItems);
54 const recipe = ExportRecipes[data.Recipe];
55 inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({
56 ItemType: ingredient.ItemType,
57 ItemCount: ingredient.ItemCount * -1
58 }));
59 addMiscItems(inventory, inventoryChanges.MiscItems);
62 60
63 if (recipe.syndicateStandingChange) {
64 const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!;
65 affiliation.Standing += recipe.syndicateStandingChange.value;
66 affiliationMods.push({
67 Tag: recipe.syndicateStandingChange.tag,
68 Standing: recipe.syndicateStandingChange.value
69 });
70 }
61 if (recipe.syndicateStandingChange) {
62 const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!;
63 affiliation.Standing += recipe.syndicateStandingChange.value;
64 affiliationMods.push({
65 Tag: recipe.syndicateStandingChange.tag,
66 Standing: recipe.syndicateStandingChange.value
67 });
71 68 }
72 69
73 70 await inventory.save();
Modified src/controllers/api/inventoryController.ts +1 -0
@@ -410,6 +410,7 @@ export const getInventoryResponse = async (
410 410 for (const equipment of inventoryResponse[key]) {
411 411 equipment.Features ??= 0;
412 412 equipment.Features |= EquipmentFeatures.ARCANE_SLOT;
413 equipment.Features |= EquipmentFeatures.SECOND_ARCANE_SLOT;
413 414 }
414 415 }
415 416 }
Added src/controllers/custom/equipmentFeaturesController.ts +34 -0
@@ -0,0 +1,34 @@
1 import type { RequestHandler } from "express";
2 import { getAccountIdForRequest } from "../../services/loginService.ts";
3 import type { TEquipmentKey } from "../../types/inventoryTypes/inventoryTypes.ts";
4 import { getInventory } from "../../services/inventoryService.ts";
5 import { EquipmentFeatures } from "../../types/equipmentTypes.ts";
6 import { sendWsBroadcastTo } from "../../services/wsService.ts";
7
8 export const equipmentFeaturesController: RequestHandler = async (req, res) => {
9 const accountId = await getAccountIdForRequest(req);
10 const category = req.query.Category as TEquipmentKey;
11 const inventory = await getInventory(
12 accountId,
13 `${category} unlockDoubleCapacityPotatoesEverywhere unlockExilusEverywhere unlockArcanesEverywhere`
14 );
15 const bit = Number(req.query.bit) as EquipmentFeatures;
16 if (
17 (inventory.unlockDoubleCapacityPotatoesEverywhere && bit === EquipmentFeatures.DOUBLE_CAPACITY) ||
18 (inventory.unlockExilusEverywhere && bit === EquipmentFeatures.UTILITY_SLOT) ||
19 (inventory.unlockArcanesEverywhere &&
20 (bit === EquipmentFeatures.ARCANE_SLOT || bit === EquipmentFeatures.SECOND_ARCANE_SLOT))
21 ) {
22 res.status(400).end();
23 }
24 const item = inventory[category].id(req.query.ItemId as string);
25 if (item) {
26 item.Features ??= 0;
27 item.Features ^= bit;
28 await inventory.save();
29 sendWsBroadcastTo(accountId, { sync_inventory: true });
30 res.status(200).end();
31 } else {
32 res.status(400).end();
33 }
34 };
Modified src/routes/custom.ts +2 -0
@@ -1,6 +1,7 @@
1 1 import express from "express";
2 2
3 3 import { tunablesController } from "../controllers/custom/tunablesController.ts";
4 import { equipmentFeaturesController } from "../controllers/custom/equipmentFeaturesController.ts";
4 5 import { getItemListsController } from "../controllers/custom/getItemListsController.ts";
5 6 import { pushArchonCrystalUpgradeController } from "../controllers/custom/pushArchonCrystalUpgradeController.ts";
6 7 import { popArchonCrystalUpgradeController } from "../controllers/custom/popArchonCrystalUpgradeController.ts";
@@ -53,6 +54,7 @@ import { getConfigController, setConfigController } from "../controllers/custom/
53 54 const customRouter = express.Router();
54 55
55 56 customRouter.get("/tunables.json", tunablesController);
57 customRouter.get("/equipmentFeatures", equipmentFeaturesController);
56 58 customRouter.get("/getItemLists", getItemListsController);
57 59 customRouter.get("/pushArchonCrystalUpgrade", pushArchonCrystalUpgradeController);
58 60 customRouter.get("/popArchonCrystalUpgrade", popArchonCrystalUpgradeController);
Modified static/webui/index.html +4 -0
@@ -840,6 +840,10 @@
840 840 </form>
841 841 </div>
842 842 </div>
843 <div id="equipmentFeatures-card" class="card mb-3 d-none">
844 <h5 class="card-header" data-loc="detailedView_equipmentFeaturesLabel"></h5>
845 <div id="equipmentFeaturesButtons-card" class="card-body d-flex flex-wrap gap-2"></div>
846 </div>
843 847 </div>
844 848 <div data-route="/webui/mods" data-title="Mods | OpenWF WebUI">
845 849 <p class="mb-3 inventory-update-note"></p>
Modified static/webui/script.js +59 -15
@@ -912,12 +912,7 @@ function updateInventory() {
912 912 td.appendChild(a);
913 913 }
914 914
915 if (
916 ["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(
917 category
918 ) ||
919 modularWeapons.includes(item.ItemType)
920 ) {
915 {
921 916 const a = document.createElement("a");
922 917 a.href =
923 918 "/webui/detailedView?productCategory=" + category + "&itemId=" + item.ItemId.$oid;
@@ -930,7 +925,7 @@ function updateInventory() {
930 925 a.href = "#";
931 926 a.onclick = function (event) {
932 927 event.preventDefault();
933 gildEquipment(category, item.ItemId.$oid);
928 equipmentFeatures(category, item.ItemId.$oid, 8);
934 929 };
935 930 a.title = loc("code_gild");
936 931 a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"/></svg>`;
@@ -1560,6 +1555,57 @@ function updateInventory() {
1560 1555 $("#detailedView-title").text(itemName);
1561 1556 }
1562 1557
1558 {
1559 document.getElementById("equipmentFeatures-card").classList.remove("d-none");
1560 const buttonsCard = document.getElementById("equipmentFeaturesButtons-card");
1561 buttonsCard.innerHTML = "";
1562 item.Features ??= 0;
1563 const bits = [];
1564 if (category != "OperatorAmps") bits.push(1);
1565 if (["Suits", "LongGuns", "Pistols", "Melee"].includes(category)) bits.push(2);
1566 if (modularWeapons.includes(item.ItemType)) bits.push(8);
1567 if (["LongGuns", "Pistols", "Melee", "SpaceGuns", "OperatorAmps"].includes(category))
1568 bits.push(32);
1569 if (category == "SpaceGuns") bits.push(4, 64);
1570 if (
1571 ["LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category) &&
1572 item.UpgradeFingerprint
1573 )
1574 bits.push(1024);
1575 for (const bit of bits.sort((a, b) => a - b)) {
1576 const wrapper = document.createElement("div");
1577 wrapper.classList = "form-check";
1578
1579 const input = document.createElement("input");
1580 input.classList = "form-check-input";
1581 input.type = "checkbox";
1582 input.id = `detailedView-feature-${bit}`;
1583 input.checked = item.Features & bit;
1584
1585 const label = document.createElement("label");
1586 label.classList = "form-check-label";
1587 label.htmlFor = input.id;
1588 label.innerHTML = loc(`code_feature_${bit}`);
1589 label.setAttribute("data-loc", `code_feature_${bit}`);
1590
1591 input.onchange = function (event) {
1592 event.preventDefault();
1593 equipmentFeatures(category, oid, bit);
1594 };
1595 if (
1596 (data.unlockDoubleCapacityPotatoesEverywhere && bit === 1) ||
1597 (data.unlockExilusEverywhere && bit === 2) ||
1598 (data.unlockArcanesEverywhere && (bit === 32 || bit === 64))
1599 ) {
1600 input.disabled = true;
1601 }
1602
1603 wrapper.appendChild(input);
1604 wrapper.appendChild(label);
1605 buttonsCard.appendChild(wrapper);
1606 }
1607 }
1608
1563 1609 if (category == "Suits") {
1564 1610 document.getElementById("archonShards-card").classList.remove("d-none");
1565 1611
@@ -2864,15 +2910,11 @@ function disposeOfItems(category, type, count) {
2864 2910 });
2865 2911 }
2866 2912
2867 function gildEquipment(category, oid) {
2913 function equipmentFeatures(category, oid, bit) {
2868 2914 revalidateAuthz().then(() => {
2869 $.post({
2870 url: "/api/gildWeapon.php?" + window.authz + "&ItemId=" + oid + "&Category=" + category,
2871 contentType: "application/octet-stream",
2872 data: JSON.stringify({
2873 Recipe: "webui"
2874 })
2875 }).done(function () {
2915 $.get(
2916 "/custom/equipmentFeatures?" + window.authz + "&ItemId=" + oid + "&Category=" + category + "&bit=" + bit
2917 ).done(function () {
2876 2918 updateInventory();
2877 2919 });
2878 2920 });
@@ -3478,6 +3520,8 @@ single.getRoute("#detailedView-route").on("beforeload", function () {
3478 3520 document.getElementById("modularParts-card").classList.add("d-none");
3479 3521 document.getElementById("modularParts-form").innerHTML = "";
3480 3522 document.getElementById("valenceBonus-card").classList.add("d-none");
3523 document.getElementById("equipmentFeatures-card").classList.add("d-none");
3524 document.getElementById("equipmentFeaturesButtons-card").innerHTML = "";
3481 3525 if (window.didInitialInventoryUpdate) {
3482 3526 updateInventory();
3483 3527 }
Modified static/webui/translations/de.js +8 -0
@@ -81,6 +81,13 @@ dict = {
81 81 code_operatorFaceName: `Operator-Gesicht: |INDEX|`,
82 82 code_succChange: `Erfolgreich geändert.`,
83 83 code_requiredInvigorationUpgrade: `Du musst sowohl ein Offensiv- als auch ein Support-Upgrade auswählen.`,
84 code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
85 code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
86 code_feature_4: `[UNTRANSLATED] Gravimag`,
87 code_feature_8: `[UNTRANSLATED] Gild`,
88 code_feature_32: `[UNTRANSLATED] Arcane Slot`,
89 code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
90 code_feature_1024: `[UNTRANSLATED] Valence Override`,
84 91 login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`,
85 92 login_emailLabel: `E-Mail-Adresse`,
86 93 login_passwordLabel: `Passwort`,
@@ -155,6 +162,7 @@ dict = {
155 162 detailedView_modularPartsLabel: `Modulare Teile ändern`,
156 163 detailedView_invigorationLabel: `Kräftigung`,
157 164 detailedView_loadoutLabel: `Loadouts`,
165 detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
158 166
159 167 invigorations_offensive_AbilityStrength: `+200% Fähigkeitsstärke`,
160 168 invigorations_offensive_AbilityRange: `+100% Fähigkeitsreichweite`,
Modified static/webui/translations/en.js +8 -0
@@ -80,6 +80,13 @@ dict = {
80 80 code_operatorFaceName: `Operator Visage |INDEX|`,
81 81 code_succChange: `Successfully changed.`,
82 82 code_requiredInvigorationUpgrade: `You must select both an offensive & utility upgrade.`,
83 code_feature_1: `Orokin Reactor`,
84 code_feature_2: `Exilus Adapter`,
85 code_feature_4: `Gravimag`,
86 code_feature_8: `Gild`,
87 code_feature_32: `Arcane Slot`,
88 code_feature_64: `Second Arcane Slot`,
89 code_feature_1024: `Valence Override`,
83 90 login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`,
84 91 login_emailLabel: `Email address`,
85 92 login_passwordLabel: `Password`,
@@ -154,6 +161,7 @@ dict = {
154 161 detailedView_modularPartsLabel: `Change Modular Parts`,
155 162 detailedView_invigorationLabel: `Invigoration`,
156 163 detailedView_loadoutLabel: `Loadouts`,
164 detailedView_equipmentFeaturesLabel: `Equipment Features`,
157 165
158 166 invigorations_offensive_AbilityStrength: `+200% Ability Strength`,
159 167 invigorations_offensive_AbilityRange: `+100% Ability Range`,
Modified static/webui/translations/es.js +8 -0
@@ -81,6 +81,13 @@ dict = {
81 81 code_operatorFaceName: `Rostro del operador |INDEX|`,
82 82 code_succChange: `Cambiado correctamente`,
83 83 code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una mejora de utilidad.`,
84 code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
85 code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
86 code_feature_4: `[UNTRANSLATED] Gravimag`,
87 code_feature_8: `[UNTRANSLATED] Gild`,
88 code_feature_32: `[UNTRANSLATED] Arcane Slot`,
89 code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
90 code_feature_1024: `[UNTRANSLATED] Valence Override`,
84 91 login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`,
85 92 login_emailLabel: `Dirección de correo electrónico`,
86 93 login_passwordLabel: `Contraseña`,
@@ -155,6 +162,7 @@ dict = {
155 162 detailedView_modularPartsLabel: `Cambiar partes modulares`,
156 163 detailedView_invigorationLabel: `Fortalecimiento`,
157 164 detailedView_loadoutLabel: `Equipamientos`,
165 detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
158 166
159 167 invigorations_offensive_AbilityStrength: `+200% Fuerza de Habilidad`,
160 168 invigorations_offensive_AbilityRange: `+100% Alcance de Habilidad`,
Modified static/webui/translations/fr.js +8 -0
@@ -81,6 +81,13 @@ dict = {
81 81 code_operatorFaceName: `Visage de l'Opérateur |INDEX|`,
82 82 code_succChange: `Changement effectué.`,
83 83 code_requiredInvigorationUpgrade: `Invigoration offensive et défensive requises.`,
84 code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
85 code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
86 code_feature_4: `[UNTRANSLATED] Gravimag`,
87 code_feature_8: `[UNTRANSLATED] Gild`,
88 code_feature_32: `[UNTRANSLATED] Arcane Slot`,
89 code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
90 code_feature_1024: `[UNTRANSLATED] Valence Override`,
84 91 login_description: `Connexion avec les informations de connexion OpenWF.`,
85 92 login_emailLabel: `Email`,
86 93 login_passwordLabel: `Mot de passe`,
@@ -155,6 +162,7 @@ dict = {
155 162 detailedView_modularPartsLabel: `Changer l'équipement modulaire`,
156 163 detailedView_invigorationLabel: `Dynamisation`,
157 164 detailedView_loadoutLabel: `Équipements`,
165 detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
158 166
159 167 invigorations_offensive_AbilityStrength: `+200% de puissance de pouvoir`,
160 168 invigorations_offensive_AbilityRange: `+100% de portée de pouvoir`,
Modified static/webui/translations/ru.js +8 -0
@@ -81,6 +81,13 @@ dict = {
81 81 code_operatorFaceName: `Внешность оператора: |INDEX|`,
82 82 code_succChange: `Успешно изменено.`,
83 83 code_requiredInvigorationUpgrade: `Вы должны выбрать как атакующее, так и вспомогательное улучшение.`,
84 code_feature_1: `Реактор Орокин`,
85 code_feature_2: `Адаптер Эксилус`,
86 code_feature_4: `Гравимаг`,
87 code_feature_8: `Улучшение`,
88 code_feature_32: `Слот Мистификатора`,
89 code_feature_64: `Второй слот Мистификатора`,
90 code_feature_1024: `Переопределение валентности`,
84 91 login_description: `Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).`,
85 92 login_emailLabel: `Адрес электронной почты`,
86 93 login_passwordLabel: `Пароль`,
@@ -155,6 +162,7 @@ dict = {
155 162 detailedView_modularPartsLabel: `Изменить модульные части`,
156 163 detailedView_invigorationLabel: `Воодушевление`,
157 164 detailedView_loadoutLabel: `Конфигурации`,
165 detailedView_equipmentFeaturesLabel: `Модификаторы снаряжения`,
158 166
159 167 invigorations_offensive_AbilityStrength: `+200% к силе способностей.`,
160 168 invigorations_offensive_AbilityRange: `+100% к зоне поражения способностей.`,
Modified static/webui/translations/uk.js +8 -0
@@ -81,6 +81,13 @@ dict = {
81 81 code_operatorFaceName: `Зовнішність оператора: |INDEX|`,
82 82 code_succChange: `Успішно змінено.`,
83 83 code_requiredInvigorationUpgrade: `Ви повинні вибрати як атакуюче, так і допоміжне вдосконалення.`,
84 code_feature_1: `[UNTRANSLATED] Orokin Reactor`,
85 code_feature_2: `[UNTRANSLATED] Exilus Adapter`,
86 code_feature_4: `[UNTRANSLATED] Gravimag`,
87 code_feature_8: `[UNTRANSLATED] Gild`,
88 code_feature_32: `[UNTRANSLATED] Arcane Slot`,
89 code_feature_64: `[UNTRANSLATED] Second Arcane Slot`,
90 code_feature_1024: `[UNTRANSLATED] Valence Override`,
84 91 login_description: `Увійдіть, використовуючи облікові дані OpenWF (ті ж, що й у грі при підключенні до цього серверу).`,
85 92 login_emailLabel: `Адреса електронної пошти`,
86 93 login_passwordLabel: `Пароль`,
@@ -155,6 +162,7 @@ dict = {
155 162 detailedView_modularPartsLabel: `Змінити модульні частини`,
156 163 detailedView_invigorationLabel: `Зміцнення`,
157 164 detailedView_loadoutLabel: `Конфігурації`,
165 detailedView_equipmentFeaturesLabel: `[UNTRANSLATED] Equipment Features`,
158 166
159 167 invigorations_offensive_AbilityStrength: `+200% до потужності здібностей.`,
160 168 invigorations_offensive_AbilityRange: `+100% до досяжності здібностей.`,
Modified static/webui/translations/zh.js +8 -0