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

SpaceNinjaServer

A simple server for a small space ninja game

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

XFEstudio/SpaceNinjaServer

feat(webui): more equipment (#826)

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

代码差异

6 个文件 +361 -218
Modified src/controllers/api/sellController.ts +42 -0
@@ -51,6 +51,41 @@ export const sellController: RequestHandler = async (req, res) => {
51 51 inventory.Melee.pull({ _id: sellItem.String });
52 52 });
53 53 }
54 if (payload.Items.SpaceSuits) {
55 payload.Items.SpaceSuits.forEach(sellItem => {
56 inventory.SpaceSuits.pull({ _id: sellItem.String });
57 });
58 }
59 if (payload.Items.SpaceGuns) {
60 payload.Items.SpaceGuns.forEach(sellItem => {
61 inventory.SpaceGuns.pull({ _id: sellItem.String });
62 });
63 }
64 if (payload.Items.SpaceMelee) {
65 payload.Items.SpaceMelee.forEach(sellItem => {
66 inventory.SpaceMelee.pull({ _id: sellItem.String });
67 });
68 }
69 if (payload.Items.Sentinels) {
70 payload.Items.Sentinels.forEach(sellItem => {
71 inventory.Sentinels.pull({ _id: sellItem.String });
72 });
73 }
74 if (payload.Items.SentinelWeapons) {
75 payload.Items.SentinelWeapons.forEach(sellItem => {
76 inventory.SentinelWeapons.pull({ _id: sellItem.String });
77 });
78 }
79 if (payload.Items.OperatorAmps) {
80 payload.Items.OperatorAmps.forEach(sellItem => {
81 inventory.OperatorAmps.pull({ _id: sellItem.String });
82 });
83 }
84 if (payload.Items.Hoverboards) {
85 payload.Items.Hoverboards.forEach(sellItem => {
86 inventory.Hoverboards.pull({ _id: sellItem.String });
87 });
88 }
54 89 if (payload.Items.Consumables) {
55 90 const consumablesChanges = [];
56 91 for (const sellItem of payload.Items.Consumables) {
@@ -110,6 +145,13 @@ interface ISellRequest {
110 145 Recipes?: ISellItem[];
111 146 Upgrades?: ISellItem[];
112 147 MiscItems?: ISellItem[];
148 SpaceSuits?: ISellItem[];
149 SpaceGuns?: ISellItem[];
150 SpaceMelee?: ISellItem[];
151 Sentinels?: ISellItem[];
152 SentinelWeapons?: ISellItem[];
153 OperatorAmps?: ISellItem[];
154 Hoverboards?: ISellItem[];
113 155 };
114 156 SellPrice: number;
115 157 SellCurrency:
Modified src/controllers/custom/addItemsController.ts +29 -10
@@ -1,8 +1,8 @@
1 1 import { getAccountIdForRequest } from "@/src/services/loginService";
2 import { getWeaponType } from "@/src/services/itemDataService";
3 import { addPowerSuit, addEquipment, getInventory, updateSlots } from "@/src/services/inventoryService";
4 import { RequestHandler } from "express";
2 import { addEquipment, addPowerSuit, getInventory, updateSlots } from "@/src/services/inventoryService";
3 import { SlotNames } from "@/src/types/purchaseTypes";
5 4 import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
5 import { RequestHandler } from "express";
6 6
7 7 export const addItemsController: RequestHandler = async (req, res) => {
8 8 const accountId = await getAccountIdForRequest(req);
@@ -10,14 +10,14 @@ export const addItemsController: RequestHandler = async (req, res) => {
10 10 const inventory = await getInventory(accountId);
11 11 for (const request of requests) {
12 12 switch (request.type) {
13 case ItemType.Powersuit:
14 updateSlots(inventory, InventorySlot.SUITS, 0, 1);
13 case ItemType.Suits:
14 updateSlots(inventory, productCategoryToSlotName[request.type], 0, 1);
15 15 addPowerSuit(inventory, request.internalName);
16 16 break;
17 17
18 case ItemType.Weapon:
19 updateSlots(inventory, InventorySlot.WEAPONS, 0, 1);
20 addEquipment(inventory, getWeaponType(request.internalName), request.internalName);
18 default:
19 updateSlots(inventory, productCategoryToSlotName[request.type], 0, 1);
20 addEquipment(inventory, request.type, request.internalName);
21 21 break;
22 22 }
23 23 }
@@ -25,9 +25,28 @@ export const addItemsController: RequestHandler = async (req, res) => {
25 25 res.end();
26 26 };
27 27
28 const productCategoryToSlotName: Record<ItemType, SlotNames> = {
29 Suits: InventorySlot.SUITS,
30 Pistols: InventorySlot.WEAPONS,
31 Melee: InventorySlot.WEAPONS,
32 LongGuns: InventorySlot.WEAPONS,
33 SpaceSuits: InventorySlot.SPACESUITS,
34 SpaceGuns: InventorySlot.SPACESUITS,
35 SpaceMelee: InventorySlot.SPACESUITS,
36 Sentinels: InventorySlot.SENTINELS,
37 SentinelWeapons: InventorySlot.SENTINELS
38 };
39
28 40 enum ItemType {
29 Powersuit = "Powersuit",
30 Weapon = "Weapon"
41 Suits = "Suits",
42 SpaceSuits = "SpaceSuits",
43 LongGuns = "LongGuns",
44 Pistols = "Pistols",
45 Melee = "Melee",
46 SpaceGuns = "SpaceGuns",
47 SpaceMelee = "SpaceMelee",
48 SentinelWeapons = "SentinelWeapons",
49 Sentinels = "Sentinels"
31 50 }
32 51
33 52 interface IAddItemRequest {
Modified src/controllers/custom/getItemListsController.ts +49 -12
@@ -5,6 +5,7 @@ import {
5 5 ExportGear,
6 6 ExportRecipes,
7 7 ExportResources,
8 ExportSentinels,
8 9 ExportUpgrades,
9 10 ExportWarframes,
10 11 ExportWeapons
@@ -15,21 +16,66 @@ interface ListedItem {
15 16 uniqueName: string;
16 17 name: string;
17 18 fusionLimit?: number;
19 exalted?: string[];
18 20 }
19 21
20 22 const getItemListsController: RequestHandler = (req, response) => {
21 23 const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
22 24 const res: Record<string, ListedItem[]> = {};
23 25 res.LongGuns = [];
24 res.Pistols = [];
25 26 res.Melee = [];
27 res.ModularParts = [];
28 res.Pistols = [];
29 res.Sentinels = [];
30 res.SentinelWeapons = [];
31 res.SpaceGuns = [];
32 res.SpaceMelee = [];
33 res.SpaceSuits = [];
34 res.Suits = [];
26 35 res.miscitems = [];
36 for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
37 if (item.productCategory == "Suits" || item.productCategory == "SpaceSuits") {
38 res[item.productCategory].push({
39 uniqueName,
40 name: getString(item.name, lang),
41 exalted: item.exalted
42 });
43 }
44 }
45 for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
46 if (item.productCategory == "Sentinels") {
47 res[item.productCategory].push({
48 uniqueName,
49 name: getString(item.name, lang)
50 });
51 }
52 }
27 53 for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
28 if (item.totalDamage !== 0) {
54 if (
55 uniqueName.split("/")[4] == "OperatorAmplifiers" ||
56 uniqueName.split("/")[5] == "SUModularSecondarySet1" ||
57 uniqueName.split("/")[5] == "SUModularPrimarySet1" ||
58 uniqueName.split("/")[5] == "InfKitGun" ||
59 uniqueName.split("/")[5] == "HoverboardParts"
60 ) {
61 res.ModularParts.push({
62 uniqueName,
63 name: getString(item.name, lang)
64 });
65 if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
66 res.miscitems.push({
67 uniqueName: "MiscItems:" + uniqueName,
68 name: getString(item.name, lang)
69 });
70 }
71 } else if (item.totalDamage !== 0) {
29 72 if (
30 73 item.productCategory == "LongGuns" ||
31 74 item.productCategory == "Pistols" ||
32 item.productCategory == "Melee"
75 item.productCategory == "Melee" ||
76 item.productCategory == "SpaceGuns" ||
77 item.productCategory == "SpaceMelee" ||
78 item.productCategory == "SentinelWeapons"
33 79 ) {
34 80 res[item.productCategory].push({
35 81 uniqueName,
@@ -102,15 +148,6 @@ const getItemListsController: RequestHandler = (req, response) => {
102 148 }
103 149
104 150 response.json({
105 warframes: Object.entries(ExportWarframes)
106 .filter(([_uniqueName, warframe]) => warframe.productCategory == "Suits")
107 .map(([uniqueName, warframe]) => {
108 return {
109 uniqueName,
110 name: getString(warframe.name, lang),
111 exalted: warframe.exalted
112 };
113 }),
114 151 badItems,
115 152 archonCrystalUpgrades,
116 153 ...res
Modified src/services/inventoryService.ts +1 -1
@@ -365,7 +365,7 @@ export const addSentinelWeapon = (
365 365 typeName: string,
366 366 inventoryChanges: IInventoryChanges
367 367 ): void => {
368 const index = inventory.SentinelWeapons.push({ ItemType: typeName }) - 1;
368 const index = inventory.SentinelWeapons.push({ ItemType: typeName, XP: 0 }) - 1;
369 369 inventoryChanges.SentinelWeapons ??= [];
370 370 (inventoryChanges.SentinelWeapons as IEquipmentClient[]).push(
371 371 inventory.SentinelWeapons[index].toJSON<IEquipmentClient>()
Modified static/webui/index.html +128 -12
@@ -99,12 +99,12 @@
99 99 <div class="card mb-3" style="height: 400px;">
100 100 <h5 class="card-header">Warframes</h5>
101 101 <div class="card-body overflow-auto">
102 <form class="input-group mb-3" onsubmit="doAcquireWarframe();return false;">
103 <input class="form-control" id="warframe-to-acquire" list="datalist-warframes" />
102 <form class="input-group mb-3" onsubmit="doAcquireEquipment('Suits');return false;">
103 <input class="form-control" id="acquire-type-Suits" list="datalist-Suits" />
104 104 <button class="btn btn-primary" type="submit">Add</button>
105 105 </form>
106 106 <table class="table table-hover w-100">
107 <tbody id="warframe-list"></tbody>
107 <tbody id="Suits-list"></tbody>
108 108 </table>
109 109 </div>
110 110 </div>
@@ -113,7 +113,7 @@
113 113 <div class="card mb-3" style="height: 400px;">
114 114 <h5 class="card-header">Primary Weapons</h5>
115 115 <div class="card-body overflow-auto">
116 <form class="input-group mb-3" onsubmit="doAcquireWeapon('LongGuns');return false;">
116 <form class="input-group mb-3" onsubmit="doAcquireEquipment('LongGuns');return false;">
117 117 <input class="form-control" id="acquire-type-LongGuns" list="datalist-LongGuns" />
118 118 <button class="btn btn-primary" type="submit">Add</button>
119 119 </form>
@@ -129,7 +129,7 @@
129 129 <div class="card mb-3" style="height: 400px;">
130 130 <h5 class="card-header">Secondary Weapons</h5>
131 131 <div class="card-body overflow-auto">
132 <form class="input-group mb-3" onsubmit="doAcquireWeapon('Pistols');return false;">
132 <form class="input-group mb-3" onsubmit="doAcquireEquipment('Pistols');return false;">
133 133 <input class="form-control" id="acquire-type-Pistols" list="datalist-Pistols" />
134 134 <button class="btn btn-primary" type="submit">Add</button>
135 135 </form>
@@ -143,7 +143,7 @@
143 143 <div class="card mb-3" style="height: 400px;">
144 144 <h5 class="card-header">Melee Weapons</h5>
145 145 <div class="card-body overflow-auto">
146 <form class="input-group mb-3" onsubmit="doAcquireWeapon('Melee');return false;">
146 <form class="input-group mb-3" onsubmit="doAcquireEquipment('Melee');return false;">
147 147 <input class="form-control" id="acquire-type-Melee" list="datalist-Melee" />
148 148 <button class="btn btn-primary" type="submit">Add</button>
149 149 </form>
@@ -154,13 +154,123 @@
154 154 </div>
155 155 </div>
156 156 </div>
157 <div class="row g-3">
158 <div class="col-lg-6">
159 <div class="card mb-3" style="height: 400px;">
160 <h5 class="card-header">Archwing</h5>
161 <div class="card-body overflow-auto">
162 <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceSuits');return false;">
163 <input class="form-control" id="acquire-type-SpaceSuits" list="datalist-SpaceSuits" />
164 <button class="btn btn-primary" type="submit">Add</button>
165 </form>
166 <table class="table table-hover w-100">
167 <tbody id="SpaceSuits-list"></tbody>
168 </table>
169 </div>
170 </div>
171 </div>
172 <div class="col-lg-6">
173 <div class="card mb-3" style="height: 400px;">
174 <h5 class="card-header">Archwing Primary Weapons</h5>
175 <div class="card-body overflow-auto">
176 <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceGuns');return false;">
177 <input class="form-control" id="acquire-type-SpaceGuns" list="datalist-SpaceGuns" />
178 <button class="btn btn-primary" type="submit">Add</button>
179 </form>
180 <table class="table table-hover w-100">
181 <tbody id="SpaceGuns-list"></tbody>
182 </table>
183 </div>
184 </div>
185 </div>
186 </div>
187 <div class="row g-3">
188 <div class="col-lg-6">
189 <div class="card mb-3" style="height: 400px;">
190 <h5 class="card-header">Archwing Melee Weapons</h5>
191 <div class="card-body overflow-auto">
192 <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceMelee');return false;">
193 <input class="form-control" id="acquire-type-SpaceMelee" list="datalist-SpaceMelee" />
194 <button class="btn btn-primary" type="submit">Add</button>
195 </form>
196 <table class="table table-hover w-100">
197 <tbody id="SpaceMelee-list"></tbody>
198 </table>
199 </div>
200 </div>
201 </div>
202 <div class="col-lg-6">
203 <div class="card mb-3" style="height: 400px;">
204 <h5 class="card-header">Sentinel Weapons</h5>
205 <div class="card-body overflow-auto">
206 <form class="input-group mb-3" onsubmit="doAcquireEquipment('SentinelWeapons');return false;">
207 <input class="form-control" id="acquire-type-SentinelWeapons" list="datalist-SentinelWeapons" />
208 <button class="btn btn-primary" type="submit">Add</button>
209 </form>
210 <table class="table table-hover w-100">
211 <tbody id="SentinelWeapons-list"></tbody>
212 </table>
213 </div>
214 </div>
215 </div>
216 </div>
217 <div class="row g-3">
218 <div class="col-lg-6">
219 <div class="card mb-3" style="height: 400px;">
220 <h5 class="card-header">Sentinels</h5>
221 <div class="card-body overflow-auto">
222 <form class="input-group mb-3" onsubmit="doAcquireEquipment('Sentinels');return false;">
223 <input class="form-control" id="acquire-type-Sentinels" list="datalist-Sentinels" />
224 <button class="btn btn-primary" type="submit">Add</button>
225 </form>
226 <table class="table table-hover w-100">
227 <tbody id="Sentinels-list"></tbody>
228 </table>
229 </div>
230 </div>
231 </div>
232 </div>
233 <div class="row g-3">
234 <div class="col-lg-6">
235 <div class="card mb-3" style="height: 400px;">
236 <h5 class="card-header">Amps</h5>
237 <div class="card-body overflow-auto">
238 <table class="table table-hover w-100">
239 <tbody id="OperatorAmps-list"></tbody>
240 </table>
241 </div>
242 </div>
243 </div>
244 <div class="col-lg-6">
245 <div class="card mb-3" style="height: 400px;">
246 <h5 class="card-header">K-Drives</h5>
247 <div class="card-body overflow-auto">
248 <table class="table table-hover w-100">
249 <tbody id="Hoverboards-list"></tbody>
250 </table>
251 </div>
252 </div>
253 </div>
254 </div>
157 255 <div class="card mb-3">
158 256 <h5 class="card-header">Bulk Actions</h5>
159 <div class="card-body d-flex flex-wrap gap-2">
160 <button class="btn btn-primary" onclick="addMissingWarframes();">Add Missing Warframes</button>
161 <button class="btn btn-primary" onclick="addMissingWeapons();">Add Missing Weapons</button>
162 <button class="btn btn-success" onclick="maxRankAllWarframes()">Max Rank All Warframes</button>
163 <button class="btn btn-success" onclick="maxRankAllWeapons()">Max Rank All Weapons</button>
257 <div class="card-body">
258 <div class="mb-2 d-flex flex-wrap gap-2">
259 <button class="btn btn-primary" onclick="addMissingEquipment(['Suits']);">Add Missing Warframes</button>
260 <button class="btn btn-primary" onclick="addMissingEquipment(['Melee', 'LongGuns', 'Pistols']);">Add Missing Weapons</button>
261 <button class="btn btn-primary" onclick="addMissingEquipment(['SpaceSuits']);">Add Missing Archwings</button>
262 <button class="btn btn-primary" onclick="addMissingEquipment(['SpaceGuns', 'SpaceMelee']);">Add Missing Archwing Weapons</button>
263 <button class="btn btn-primary" onclick="addMissingEquipment(['Sentinels']);">Add Missing Sentinels</button>
264 <button class="btn btn-primary" onclick="addMissingEquipment(['SentinelWeapons']);">Add Missing Sentinel Weapons</button>
265 </div>
266 <div class="mb-2 d-flex flex-wrap gap-2">
267 <button class="btn btn-success" onclick="maxRankAllEquipment(['Suits']);">Max Rank All Warframes</button>
268 <button class="btn btn-success" onclick="maxRankAllEquipment(['Melee', 'LongGuns', 'Pistols']);">Max Rank All Weapons</button>
269 <button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceSuits']);">Max Rank All Archwings</button>
270 <button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceGuns', 'SpaceMelee']);">Max Rank All Archwing Weapons</button>
271 <button class="btn btn-success" onclick="maxRankAllEquipment(['Sentinels']);">Max Rank All Sentinels</button>
272 <button class="btn btn-success" onclick="maxRankAllEquipment(['SentinelWeapons']);">Max Rank All Sentinel Weapons</button>
273 </div>
164 274 </div>
165 275 </div>
166 276 </div>
@@ -384,10 +494,16 @@
384 494 </div>
385 495 </div>
386 496 </div>
387 <datalist id="datalist-warframes"></datalist>
497 <datalist id="datalist-Suits"></datalist>
498 <datalist id="datalist-SpaceSuits"></datalist>
388 499 <datalist id="datalist-LongGuns"></datalist>
389 500 <datalist id="datalist-Pistols"></datalist>
390 501 <datalist id="datalist-Melee"></datalist>
502 <datalist id="datalist-SpaceGuns"></datalist>
503 <datalist id="datalist-SpaceMelee"></datalist>
504 <datalist id="datalist-SentinelWeapons"></datalist>
505 <datalist id="datalist-Sentinels"></datalist>
506 <datalist id="datalist-ModularParts"></datalist>
391 507 <datalist id="datalist-miscitems"></datalist>
392 508 <datalist id="datalist-mods">
393 509 <option data-key="/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser" value="Legendary Core"></option>
Modified static/webui/script.js +112 -183
@@ -149,6 +149,12 @@ function fetchItemList() {
149 149 "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam": { name: "Kitgun" },
150 150 "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun": { name: "Kitgun" },
151 151 "/Lotus/Weapons/Ostron/Melee/LotusModularWeapon": { name: "Zaw" },
152 "/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/OperatorTrainingAmpWeapon": {
153 name: "Mote Amp"
154 },
155 "/Lotus/Weapons/Sentients/OperatorAmplifiers/OperatorAmpWeapon": { name: "Amp" },
156 "/Lotus/Weapons/Operator/Pistols/DrifterPistol/DrifterPistolPlayerWeapon": { name: "Sirocco" },
157 "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit": { name: "K-Drive" },
152 158 // Missing in data sources
153 159 "/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser": { name: "Legendary Core" },
154 160 "/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod": { name: "Traumatic Peculiar" }
@@ -188,84 +194,19 @@ function updateInventory() {
188 194 window.didInitialInventoryUpdate = true;
189 195
190 196 // Populate inventory route
191 document.getElementById("warframe-list").innerHTML = "";
192 data.Suits.forEach(item => {
193 const tr = document.createElement("tr");
194 tr.setAttribute("data-item-type", item.ItemType);
195 {
196 const td = document.createElement("td");
197 td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
198 if (item.ItemName) {
199 td.textContent = item.ItemName + " (" + td.textContent + ")";
200 }
201 tr.appendChild(td);
202 }
203 {
204 const td = document.createElement("td");
205 td.classList = "text-end";
206 if (item.XP < 1_600_000) {
207 const a = document.createElement("a");
208 a.href = "#";
209 a.onclick = function (event) {
210 event.preventDefault();
211 addGearExp("Suits", item.ItemId.$oid, 1_600_000 - item.XP);
212 if ("exalted" in itemMap[item.ItemType]) {
213 for (const exaltedType of itemMap[item.ItemType].exalted) {
214 const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType);
215 if (exaltedItem) {
216 const exaltedCap =
217 itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
218 if (exaltedItem.XP < exaltedCap) {
219 addGearExp(
220 "SpecialItems",
221 exaltedItem.ItemId.$oid,
222 exaltedCap - exaltedItem.XP
223 );
224 }
225 }
226 }
227 }
228 };
229 a.title = "Make Rank 30";
230 a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`;
231 td.appendChild(a);
232 }
233 {
234 const a = document.createElement("a");
235 a.href = "/webui/powersuit/" + item.ItemId.$oid;
236 a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>`;
237 td.appendChild(a);
238 }
239 {
240 const a = document.createElement("a");
241 a.href = "#";
242 a.onclick = function (event) {
243 event.preventDefault();
244 const name = prompt("Enter new custom name:");
245 if (name !== null) {
246 renameGear("Suits", item.ItemId.$oid, name);
247 }
248 };
249 a.title = "Rename";
250 a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 80V229.5c0 17 6.7 33.3 18.7 45.3l176 176c25 25 65.5 25 90.5 0L418.7 317.3c25-25 25-65.5 0-90.5l-176-176c-12-12-28.3-18.7-45.3-18.7H48C21.5 32 0 53.5 0 80zm112 32a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>`;
251 td.appendChild(a);
252 }
253 {
254 const a = document.createElement("a");
255 a.href = "#";
256 a.onclick = function (event) {
257 event.preventDefault();
258 disposeOfGear("Suits", item.ItemId.$oid);
259 };
260 a.title = "Remove";
261 a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>`;
262 td.appendChild(a);
263 }
264 tr.appendChild(td);
265 }
266 document.getElementById("warframe-list").appendChild(tr);
267 });
268 ["LongGuns", "Pistols", "Melee"].forEach(category => {
197 [
198 "Suits",
199 "SpaceSuits",
200 "Sentinels",
201 "LongGuns",
202 "Pistols",
203 "Melee",
204 "SpaceGuns",
205 "SpaceMelee",
206 "SentinelWeapons",
207 "Hoverboards",
208 "OperatorAmps"
209 ].forEach(category => {
269 210 document.getElementById(category + "-list").innerHTML = "";
270 211 data[category].forEach(item => {
271 212 const tr = document.createElement("tr");
@@ -276,22 +217,59 @@ function updateInventory() {
276 217 if (item.ItemName) {
277 218 td.textContent = item.ItemName + " (" + td.textContent + ")";
278 219 }
220 if (item.ModularParts) {
221 td.textContent += " [";
222 item.ModularParts.forEach(part => {
223 td.textContent += " " + (itemMap[part]?.name ?? part) + ",";
224 });
225 td.textContent = td.textContent.slice(0, -1) + " ]";
226 }
279 227 tr.appendChild(td);
280 228 }
281 229 {
282 230 const td = document.createElement("td");
283 231 td.classList = "text-end";
284 if (item.XP < 800_000) {
232 const maxXP =
233 category === "Suits" ||
234 category === "SpaceSuits" ||
235 category === "Sentinels" ||
236 category === "Hoverboards"
237 ? 1_600_000
238 : 800_000;
239
240 if (item.XP < maxXP) {
285 241 const a = document.createElement("a");
286 242 a.href = "#";
287 243 a.onclick = function (event) {
288 244 event.preventDefault();
289 addGearExp(category, item.ItemId.$oid, 800_000 - item.XP);
245 addGearExp(category, item.ItemId.$oid, maxXP - item.XP);
246 if ("exalted" in itemMap[item.ItemType]) {
247 for (const exaltedType of itemMap[item.ItemType].exalted) {
248 const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType);
249 if (exaltedItem) {
250 const exaltedCap =
251 itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
252 if (exaltedItem.XP < exaltedCap) {
253 addGearExp(
254 "SpecialItems",
255 exaltedItem.ItemId.$oid,
256 exaltedCap - exaltedItem.XP
257 );
258 }
259 }
260 }
261 }
290 262 };
291 263 a.title = "Make Rank 30";
292 264 a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`;
293 265 td.appendChild(a);
294 266 }
267 if (category == "Suits") {
268 const a = document.createElement("a");
269 a.href = "/webui/powersuit/" + item.ItemId.$oid;
270 a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>`;
271 td.appendChild(a);
272 }
295 273 {
296 274 const a = document.createElement("a");
297 275 a.href = "#";
@@ -524,35 +502,7 @@ function getKey(input) {
524 502 ?.getAttribute("data-key");
525 503 }
526 504
527 function doAcquireWarframe() {
528 const uniqueName = getKey(document.getElementById("warframe-to-acquire"));
529 if (!uniqueName) {
530 $("#warframe-to-acquire").addClass("is-invalid").focus();
531 return;
532 }
533 revalidateAuthz(() => {
534 const req = $.post({
535 url: "/custom/addItems?" + window.authz,
536 contentType: "application/json",
537 data: JSON.stringify([
538 {
539 type: "Powersuit",
540 internalName: uniqueName
541 }
542 ])
543 });
544 req.done(() => {
545 document.getElementById("warframe-to-acquire").value = "";
546 updateInventory();
547 });
548 });
549 }
550
551 $("input[list]").on("input", function () {
552 $(this).removeClass("is-invalid");
553 });
554
555 function doAcquireWeapon(category) {
505 function doAcquireEquipment(category) {
556 506 const uniqueName = getKey(document.getElementById("acquire-type-" + category));
557 507 if (!uniqueName) {
558 508 $("#acquire-type-" + category)
@@ -566,7 +516,7 @@ function doAcquireWeapon(category) {
566 516 contentType: "application/json",
567 517 data: JSON.stringify([
568 518 {
569 type: "Weapon",
519 type: category,
570 520 internalName: uniqueName
571 521 }
572 522 ])
@@ -578,6 +528,10 @@ function doAcquireWeapon(category) {
578 528 });
579 529 }
580 530
531 $("input[list]").on("input", function () {
532 $(this).removeClass("is-invalid");
533 });
534
581 535 function dispatchAddItemsRequestsBatch(requests) {
582 536 revalidateAuthz(() => {
583 537 const req = $.post({
@@ -591,12 +545,18 @@ function dispatchAddItemsRequestsBatch(requests) {
591 545 });
592 546 }
593 547
594 function addMissingWarframes() {
548 function addMissingEquipment(categories) {
595 549 const requests = [];
596 document.querySelectorAll("#datalist-warframes option").forEach(elm => {
597 if (!document.querySelector("#warframe-list [data-item-type='" + elm.getAttribute("data-key") + "']")) {
598 requests.push({ type: "Powersuit", internalName: elm.getAttribute("data-key") });
599 }
550 categories.forEach(category => {
551 document.querySelectorAll("#datalist-" + category + " option").forEach(elm => {
552 if (
553 !document.querySelector(
554 "#" + category + "-list [data-item-type='" + elm.getAttribute("data-key") + "']"
555 )
556 ) {
557 requests.push({ type: category, internalName: elm.getAttribute("data-key") });
558 }
559 });
600 560 });
601 561 if (
602 562 requests.length != 0 &&
@@ -606,88 +566,57 @@ function addMissingWarframes() {
606 566 }
607 567 }
608 568
609 function maxRankAllWarframes() {
569 function maxRankAllEquipment(categories) {
610 570 const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
611 571
612 572 req.done(data => {
613 573 window.itemListPromise.then(itemMap => {
614 const batchData = { Suits: [], SpecialItems: [] };
615
616 data.Suits.forEach(item => {
617 if (item.XP < 1_600_000) {
618 batchData.Suits.push({
619 ItemId: { $oid: item.ItemId.$oid },
620 XP: 1_600_000 - item.XP
621 });
622 }
574 const batchData = {};
623 575
624 if ("exalted" in itemMap[item.ItemType]) {
625 for (const exaltedType of itemMap[item.ItemType].exalted) {
626 const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType);
627 if (exaltedItem) {
628 const exaltedCap = itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
629 if (exaltedItem.XP < exaltedCap) {
630 batchData.SpecialItems.push({
631 ItemId: { $oid: exaltedItem.ItemId.$oid },
632 XP: exaltedCap
633 });
576 categories.forEach(category => {
577 data[category].forEach(item => {
578 const maxXP =
579 category === "Suits" ||
580 category === "SpaceSuits" ||
581 category === "Sentinels" ||
582 category === "Hoverboards"
583 ? 1_600_000
584 : 800_000;
585
586 if (item.XP < maxXP) {
587 if (!batchData[category]) {
588 batchData[category] = [];
589 }
590 batchData[category].push({
591 ItemId: { $oid: item.ItemId.$oid },
592 XP: maxXP
593 });
594 }
595 if (category === "Suits") {
596 if ("exalted" in itemMap[item.ItemType]) {
597 for (const exaltedType of itemMap[item.ItemType].exalted) {
598 const exaltedItem = data["SpecialItems"].find(x => x.ItemType == exaltedType);
599 if (exaltedItem) {
600 const exaltedCap = itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
601 if (exaltedItem.XP < exaltedCap) {
602 batchData["SpecialItems"].push({
603 ItemId: { $oid: exaltedItem.ItemId.$oid },
604 XP: exaltedCap
605 });
606 }
607 }
634 608 }
635 609 }
636 610 }
637 }
611 });
638 612 });
639 613
640 if (batchData.Suits.length > 0 || batchData.SpecialItems.length > 0) {
614 if (Object.keys(batchData).length > 0) {
641 615 return sendBatchGearExp(batchData);
642 616 }
643 617
644 alert("No Warframes to rank up.");
645 });
646 });
647 }
648
649 function addMissingWeapons() {
650 const requests = [];
651 document
652 .querySelectorAll("#datalist-LongGuns option, #datalist-Pistols option, #datalist-Melee option")
653 .forEach(elm => {
654 if (!document.querySelector("#weapon-list [data-item-type='" + elm.getAttribute("data-key") + "']")) {
655 requests.push({ type: "Weapon", internalName: elm.getAttribute("data-key") });
656 }
618 alert("No equipment to rank up.");
657 619 });
658 if (
659 requests.length != 0 &&
660 window.confirm("Are you sure you want to add " + requests.length + " items to your account?")
661 ) {
662 dispatchAddItemsRequestsBatch(requests);
663 }
664 }
665
666 function maxRankAllWeapons() {
667 const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
668
669 req.done(data => {
670 const batchData = {};
671
672 ["LongGuns", "Pistols", "Melee"].forEach(category => {
673 data[category].forEach(item => {
674 if (item.XP < 800_000) {
675 if (!batchData[category]) {
676 batchData[category] = [];
677 }
678 batchData[category].push({
679 ItemId: { $oid: item.ItemId.$oid },
680 XP: 800_000 - item.XP
681 });
682 }
683 });
684 });
685
686 if (Object.keys(batchData).length > 0) {
687 return sendBatchGearExp(batchData);
688 }
689
690 alert("No weapons to rank up.");
691 620 });
692 621 }
693 622