feat(webui): adding modular K-Drives, Amps and Zaw #1374

Merged
Sainan merged 4 commits from AMelonInsideLemon/SpaceNinjaServer:webui-modular into main 2025-03-30 05:10:25 -07:00
10 changed files with 193 additions and 15 deletions

View File

@ -0,0 +1,21 @@
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory, addEquipment, occupySlot, productCategoryToInventoryBin } from "@/src/services/inventoryService";
import { RequestHandler } from "express";
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
export const addModularEquipmentController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const request = req.body as IAddModularEquipmentRequest;
const category = modularWeaponTypes[request.ItemType];
const inventoryBin = productCategoryToInventoryBin(category)!;
const inventory = await getInventory(accountId, `${category} ${inventoryBin}`);
addEquipment(inventory, category, request.ItemType, request.ModularParts);
occupySlot(inventory, inventoryBin, true);
await inventory.save();
res.end();
};
interface IAddModularEquipmentRequest {
ItemType: string;
ModularParts: string[];
}

View File

@ -25,6 +25,7 @@ interface ListedItem {
fusionLimit?: number; fusionLimit?: number;
exalted?: string[]; exalted?: string[];
badReason?: "starter" | "frivolous" | "notraw"; badReason?: "starter" | "frivolous" | "notraw";
partType?: string;
} }
const relicQualitySuffixes: Record<TRelicQuality, string> = { const relicQualitySuffixes: Record<TRelicQuality, string> = {
@ -50,6 +51,7 @@ const getItemListsController: RequestHandler = (req, response) => {
res.MechSuits = []; res.MechSuits = [];
res.miscitems = []; res.miscitems = [];
res.Syndicates = []; res.Syndicates = [];
res.OperatorAmps = [];
for (const [uniqueName, item] of Object.entries(ExportWarframes)) { for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
res[item.productCategory].push({ res[item.productCategory].push({
uniqueName, uniqueName,
@ -79,7 +81,8 @@ const getItemListsController: RequestHandler = (req, response) => {
) { ) {
res.ModularParts.push({ res.ModularParts.push({
uniqueName, uniqueName,
name: getString(item.name, lang) name: getString(item.name, lang),
partType: item.partType
}); });
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") { if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
res.miscitems.push({ res.miscitems.push({
@ -94,7 +97,8 @@ const getItemListsController: RequestHandler = (req, response) => {
item.productCategory == "Melee" || item.productCategory == "Melee" ||
item.productCategory == "SpaceGuns" || item.productCategory == "SpaceGuns" ||
item.productCategory == "SpaceMelee" || item.productCategory == "SpaceMelee" ||
item.productCategory == "SentinelWeapons" item.productCategory == "SentinelWeapons" ||
item.productCategory == "OperatorAmps"
) { ) {
res[item.productCategory].push({ res[item.productCategory].push({
uniqueName, uniqueName,

View File

@ -13,8 +13,9 @@ import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAl
import { createAccountController } from "@/src/controllers/custom/createAccountController"; import { createAccountController } from "@/src/controllers/custom/createAccountController";
import { createMessageController } from "@/src/controllers/custom/createMessageController"; import { createMessageController } from "@/src/controllers/custom/createMessageController";
import { addCurrencyController } from "../controllers/custom/addCurrencyController"; import { addCurrencyController } from "@/src/controllers/custom/addCurrencyController";
import { addItemsController } from "@/src/controllers/custom/addItemsController"; import { addItemsController } from "@/src/controllers/custom/addItemsController";
import { addModularEquipmentController } from "@/src/controllers/custom/addModularEquipmentController";
import { addXpController } from "@/src/controllers/custom/addXpController"; import { addXpController } from "@/src/controllers/custom/addXpController";
import { importController } from "@/src/controllers/custom/importController"; import { importController } from "@/src/controllers/custom/importController";
@ -39,6 +40,7 @@ customRouter.post("/createAccount", createAccountController);
customRouter.post("/createMessage", createMessageController); customRouter.post("/createMessage", createMessageController);
customRouter.post("/addCurrency", addCurrencyController); customRouter.post("/addCurrency", addCurrencyController);
customRouter.post("/addItems", addItemsController); customRouter.post("/addItems", addItemsController);
customRouter.post("/addModularEquipment", addModularEquipmentController);
customRouter.post("/addXp", addXpController); customRouter.post("/addXp", addXpController);
customRouter.post("/import", importController); customRouter.post("/import", importController);
customRouter.post("/manageQuests", manageQuestsController); customRouter.post("/manageQuests", manageQuestsController);

View File

@ -193,10 +193,15 @@
<div class="card mb-3" style="height: 400px;"> <div class="card mb-3" style="height: 400px;">
<h5 class="card-header" data-loc="inventory_melee"></h5> <h5 class="card-header" data-loc="inventory_melee"></h5>
<div class="card-body overflow-auto"> <div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireEquipment('Melee');return false;"> <form class="input-group mb-3" onsubmit="handleModularSelection('Melee');return false;">
<input class="form-control" id="acquire-type-Melee" list="datalist-Melee" /> <input class="form-control" id="acquire-type-Melee" list="datalist-Melee" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button> <button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form> </form>
<form class="input-group mb-3" id="modular-Melee" style="display: none;">
<input class="form-control" id="acquire-type-Melee-BLADE" list="datalist-ModularParts-BLADE" />
<input class="form-control" id="acquire-type-Melee-HILT" list="datalist-ModularParts-HILT" />
<input class="form-control" id="acquire-type-Melee-HILT_WEIGHT" list="datalist-ModularParts-HILT_WEIGHT" />
</form>
<table class="table table-hover w-100"> <table class="table table-hover w-100">
<tbody id="Melee-list"></tbody> <tbody id="Melee-list"></tbody>
</table> </table>
@ -299,6 +304,15 @@
<div class="card mb-3" style="height: 400px;"> <div class="card mb-3" style="height: 400px;">
<h5 class="card-header" data-loc="inventory_operatorAmps"></h5> <h5 class="card-header" data-loc="inventory_operatorAmps"></h5>
<div class="card-body overflow-auto"> <div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="handleModularSelection('OperatorAmps');return false;">
<input class="form-control" id="acquire-type-OperatorAmps" list="datalist-OperatorAmps" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form>
<form class="input-group mb-3" id="modular-OperatorAmps" style="display: none;">
<input class="form-control" id="acquire-type-OperatorAmps-AMP_OCULUS" list="datalist-ModularParts-AMP_OCULUS" />
<input class="form-control" id="acquire-type-OperatorAmps-AMP_CORE" list="datalist-ModularParts-AMP_CORE" />
<input class="form-control" id="acquire-type-OperatorAmps-AMP_BRACE" list="datalist-ModularParts-AMP_BRACE" />
</form>
<table class="table table-hover w-100"> <table class="table table-hover w-100">
<tbody id="OperatorAmps-list"></tbody> <tbody id="OperatorAmps-list"></tbody>
</table> </table>
@ -309,6 +323,13 @@
<div class="card mb-3" style="height: 400px;"> <div class="card mb-3" style="height: 400px;">
<h5 class="card-header" data-loc="inventory_hoverboards"></h5> <h5 class="card-header" data-loc="inventory_hoverboards"></h5>
<div class="card-body overflow-auto"> <div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireModularEquipment('HoverBoards');return false;">
<input class="form-control" id="acquire-type-HoverBoards-HB_DECK" list="datalist-ModularParts-HB_DECK" />
<input class="form-control" id="acquire-type-HoverBoards-HB_ENGINE" list="datalist-ModularParts-HB_ENGINE" />
<input class="form-control" id="acquire-type-HoverBoards-HB_FRONT" list="datalist-ModularParts-HB_FRONT" />
<input class="form-control" id="acquire-type-HoverBoards-HB_JET" list="datalist-ModularParts-HB_JET" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form>
<table class="table table-hover w-100"> <table class="table table-hover w-100">
<tbody id="Hoverboards-list"></tbody> <tbody id="Hoverboards-list"></tbody>
</table> </table>
@ -604,7 +625,6 @@
<datalist id="datalist-SpaceMelee"></datalist> <datalist id="datalist-SpaceMelee"></datalist>
<datalist id="datalist-SentinelWeapons"></datalist> <datalist id="datalist-SentinelWeapons"></datalist>
<datalist id="datalist-Sentinels"></datalist> <datalist id="datalist-Sentinels"></datalist>
<datalist id="datalist-ModularParts"></datalist>
<datalist id="datalist-MechSuits"></datalist> <datalist id="datalist-MechSuits"></datalist>
<datalist id="datalist-Syndicates"></datalist> <datalist id="datalist-Syndicates"></datalist>
<datalist id="datalist-miscitems"></datalist> <datalist id="datalist-miscitems"></datalist>
@ -613,6 +633,18 @@
<option data-key="/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod" value="Traumatic Peculiar"></option> <option data-key="/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod" value="Traumatic Peculiar"></option>
</datalist> </datalist>
<datalist id="datalist-archonCrystalUpgrades"></datalist> <datalist id="datalist-archonCrystalUpgrades"></datalist>
<datalist id="datalist-OperatorAmps"></datalist>
<datalist id="datalist-ModularParts"></datalist>
<datalist id="datalist-ModularParts-HB_DECK"></datalist>
<datalist id="datalist-ModularParts-HB_ENGINE"></datalist>
<datalist id="datalist-ModularParts-HB_FRONT"></datalist>
<datalist id="datalist-ModularParts-HB_JET"></datalist>
<datalist id="datalist-ModularParts-AMP_OCULUS"></datalist>
<datalist id="datalist-ModularParts-AMP_CORE"></datalist>
<datalist id="datalist-ModularParts-AMP_BRACE"></datalist>
<datalist id="datalist-ModularParts-BLADE"></datalist>
<datalist id="datalist-ModularParts-HILT"></datalist>
<datalist id="datalist-ModularParts-HILT_WEIGHT"></datalist>
<script src="/webui/libs/jquery-3.6.0.min.js"></script> <script src="/webui/libs/jquery-3.6.0.min.js"></script>
<script src="/webui/libs/whirlpool-js.min.js"></script> <script src="/webui/libs/whirlpool-js.min.js"></script>
<script src="/webui/libs/single.js"></script> <script src="/webui/libs/single.js"></script>

View File

@ -185,6 +185,16 @@ function fetchItemList() {
name: loc("code_traumaticPeculiar") name: loc("code_traumaticPeculiar")
}); });
// Add modular weapons
data.OperatorAmps.push({
uniqueName: "/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon",
name: loc("code_amp")
});
data.Melee.push({
uniqueName: "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon",
name: loc("code_zaw")
});
const itemMap = { const itemMap = {
// Generics for rivens // Generics for rivens
"/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": { name: loc("code_archgun") }, "/Lotus/Weapons/Tenno/Archwing/Primary/ArchGun": { name: loc("code_archgun") },
@ -201,14 +211,9 @@ function fetchItemList() {
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": { name: loc("code_kitgun") }, "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary": { name: loc("code_kitgun") },
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: loc("code_kitgun") }, "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: loc("code_kitgun") },
"/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: loc("code_kitgun") }, "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: loc("code_kitgun") },
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: loc("code_zaw") },
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/OperatorTrainingAmpWeapon": { "/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/OperatorTrainingAmpWeapon": {
name: loc("code_moteAmp") name: loc("code_moteAmp")
}, },
"/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon": { name: loc("code_amp") },
"/Lotus/Weapons/Operator/Pistols/DrifterPistol/DrifterPistolPlayerWeapon": {
name: loc("code_sirocco")
},
"/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit": { name: loc("code_kDrive") } "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit": { name: loc("code_kDrive") }
}; };
for (const [type, items] of Object.entries(data)) { for (const [type, items] of Object.entries(data)) {
@ -233,6 +238,33 @@ function fetchItemList() {
if (type == "Syndicates" && item.uniqueName.startsWith("RadioLegion")) { if (type == "Syndicates" && item.uniqueName.startsWith("RadioLegion")) {
item.name += " (" + item.uniqueName + ")"; item.name += " (" + item.uniqueName + ")";
} }
if (type == "ModularParts") {
const supportedModularParts = [
"LWPT_HB_DECK",
"LWPT_HB_ENGINE",
"LWPT_HB_FRONT",
"LWPT_HB_JET",
"LWPT_AMP_OCULUS",
"LWPT_AMP_CORE",
"LWPT_AMP_BRACE",
"LWPT_BLADE",
"LWPT_HILT",
"LWPT_HILT_WEIGHT"
];
if (supportedModularParts.includes(item.partType)) {
const option = document.createElement("option");
option.setAttribute("data-key", item.uniqueName);
option.value = item.name;
document
.getElementById("datalist-" + type + "-" + item.partType.slice(5))
.appendChild(option);
} else {
const option = document.createElement("option");
option.setAttribute("data-key", item.uniqueName);
option.value = item.name;
document.getElementById("datalist-" + type).appendChild(option);
}
}
if (item.badReason != "notraw") { if (item.badReason != "notraw") {
const option = document.createElement("option"); const option = document.createElement("option");
option.setAttribute("data-key", item.uniqueName); option.setAttribute("data-key", item.uniqueName);
@ -622,6 +654,67 @@ function doAcquireEquipment(category) {
}); });
} }
function doAcquireModularEquipment(category, ItemType) {
let requiredParts;
let ModularParts = [];
switch (category) {
case "HoverBoards":
ItemType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
requiredParts = ["HB_DECK", "HB_ENGINE", "HB_FRONT", "HB_JET"];
break;
case "OperatorAmps":
requiredParts = ["AMP_OCULUS", "AMP_CORE", "AMP_BRACE"];
break;
case "Melee":
requiredParts = ["BLADE", "HILT", "HILT_WEIGHT"];
break;
}
requiredParts.forEach(part => {
const partName = getKey(document.getElementById("acquire-type-" + category + "-" + part));
if (partName) {
ModularParts.push(partName);
}
});
if (ModularParts.length != requiredParts.length) {
let isFirstPart = true;
requiredParts.forEach(part => {
const partSelector = document.getElementById("acquire-type-" + category + "-" + part);
if (!getKey(partSelector)) {
if (isFirstPart) {
isFirstPart = false;
$("#acquire-type-" + category + "-" + part)
.addClass("is-invalid")
.focus();
} else {
$("#acquire-type-" + category + "-" + part).addClass("is-invalid");
}
}
});
} else {
revalidateAuthz(() => {
const req = $.post({
url: "/custom/addModularEquipment?" + window.authz,
contentType: "application/json",
data: JSON.stringify({
ItemType,
ModularParts
})
});
req.done(() => {
const mainInput = document.getElementById("acquire-type-" + category);
if (mainInput) {
mainInput.value = "";
document.getElementById("modular-" + category).style.display = "none";
}
requiredParts.forEach(part => {
document.getElementById("acquire-type-" + category + "-" + part).value = "";
});
updateInventory();
});
});
}
}
$("input[list]").on("input", function () { $("input[list]").on("input", function () {
$(this).removeClass("is-invalid"); $(this).removeClass("is-invalid");
}); });
@ -1220,3 +1313,34 @@ function toast(text) {
toast.appendChild(div); toast.appendChild(div);
new bootstrap.Toast(document.querySelector(".toast-container").appendChild(toast)).show(); new bootstrap.Toast(document.querySelector(".toast-container").appendChild(toast)).show();
} }
function handleModularSelection(category) {
const modularWeapons = [
"/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon",
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon"
];
const itemType = getKey(document.getElementById("acquire-type-" + category));
if (modularWeapons.includes(itemType)) {
doAcquireModularEquipment(category, itemType);
} else {
doAcquireEquipment(category);
}
}
{
const modularWeapons = [
"/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon",
"/Lotus/Weapons/Ostron/Melee/LotusModularWeapon"
];
const supportedModularInventoryCategory = ["OperatorAmps", "Melee"];
supportedModularInventoryCategory.forEach(inventoryCategory => {
document.getElementById("acquire-type-" + inventoryCategory).addEventListener("input", function () {
const modularFields = document.getElementById("modular-" + inventoryCategory);
if (modularWeapons.includes(getKey(this))) {
modularFields.style.display = "";
} else {
modularFields.style.display = "none";
}
});
});
}

View File

@ -15,7 +15,6 @@ dict = {
code_zaw: `Zaw`, code_zaw: `Zaw`,
code_moteAmp: `Anfangsverstärker`, code_moteAmp: `Anfangsverstärker`,
code_amp: `Verstärker`, code_amp: `Verstärker`,
code_sirocco: `Sirocco`,
code_kDrive: `K-Drive`, code_kDrive: `K-Drive`,
code_legendaryCore: `Legendärer Kern`, code_legendaryCore: `Legendärer Kern`,
code_traumaticPeculiar: `Kuriose Mod: Traumatisch`, code_traumaticPeculiar: `Kuriose Mod: Traumatisch`,

View File

@ -14,7 +14,6 @@ dict = {
code_zaw: `Zaw`, code_zaw: `Zaw`,
code_moteAmp: `Mote Amp`, code_moteAmp: `Mote Amp`,
code_amp: `Amp`, code_amp: `Amp`,
code_sirocco: `Sirocco`,
code_kDrive: `K-Drive`, code_kDrive: `K-Drive`,
code_legendaryCore: `Legendary Core`, code_legendaryCore: `Legendary Core`,
code_traumaticPeculiar: `Traumatic Peculiar`, code_traumaticPeculiar: `Traumatic Peculiar`,

View File

@ -15,7 +15,6 @@ dict = {
code_zaw: `Zaw`, code_zaw: `Zaw`,
code_moteAmp: `Amplificateur Faible`, code_moteAmp: `Amplificateur Faible`,
code_amp: `Amplificateur`, code_amp: `Amplificateur`,
code_sirocco: `Sirocco`,
code_kDrive: `K-Drive`, code_kDrive: `K-Drive`,
code_legendaryCore: `Coeur Légendaire`, code_legendaryCore: `Coeur Légendaire`,
code_traumaticPeculiar: `Traumatisme Atypique`, code_traumaticPeculiar: `Traumatisme Atypique`,

View File

@ -15,7 +15,6 @@ dict = {
code_zaw: `Зо`, code_zaw: `Зо`,
code_moteAmp: `Пылинка`, code_moteAmp: `Пылинка`,
code_amp: `Усилитель`, code_amp: `Усилитель`,
code_sirocco: `Сирокко`,
code_kDrive: `К-Драйв`, code_kDrive: `К-Драйв`,
code_legendaryCore: `Легендарное ядро`, code_legendaryCore: `Легендарное ядро`,
code_traumaticPeculiar: `Травмирующая Странность`, code_traumaticPeculiar: `Травмирующая Странность`,

View File

@ -15,7 +15,6 @@ dict = {
code_zaw: `自制近战`, code_zaw: `自制近战`,
code_moteAmp: `微尘增幅器`, code_moteAmp: `微尘增幅器`,
code_amp: `增幅器`, code_amp: `增幅器`,
code_sirocco: `赤风`,
code_kDrive: `K式悬浮板`, code_kDrive: `K式悬浮板`,
code_legendaryCore: `传奇核心`, code_legendaryCore: `传奇核心`,
code_traumaticPeculiar: `创伤怪奇`, code_traumaticPeculiar: `创伤怪奇`,