Compare commits

...

4 Commits

Author SHA1 Message Date
9662da00de chore(webui): update uk & ru (#2728)
Reviewed-on: OpenWF/SpaceNinjaServer#2728
Co-authored-by: LoseFace <loseface@noreply.localhost>
Co-committed-by: LoseFace <loseface@noreply.localhost>
2025-08-30 19:35:06 -07:00
662d824369 chore: move unlockAllSimarisResearchEntries to a per-account button (#2726)
Closes #2725. Re #2361.

Reviewed-on: OpenWF/SpaceNinjaServer#2726
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-08-30 19:34:55 -07:00
a0bac12e95 fix: put vault medallion into correct place (#2723)
Re #2719

Reviewed-on: OpenWF/SpaceNinjaServer#2723
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>
2025-08-30 19:34:30 -07:00
e98cb2ec24 feat(webui): add item by ItemType (#2704)
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2704
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>
2025-08-30 19:34:22 -07:00
16 changed files with 177 additions and 53 deletions

View File

@ -486,19 +486,6 @@ export const getInventoryResponse = async (
}
}
if (config.unlockAllSimarisResearchEntries) {
inventoryResponse.LibraryPersonalTarget = undefined;
inventoryResponse.LibraryPersonalProgress = [
"/Lotus/Types/Game/Library/Targets/Research1Target",
"/Lotus/Types/Game/Library/Targets/Research2Target",
"/Lotus/Types/Game/Library/Targets/Research3Target",
"/Lotus/Types/Game/Library/Targets/Research4Target",
"/Lotus/Types/Game/Library/Targets/Research5Target",
"/Lotus/Types/Game/Library/Targets/Research6Target",
"/Lotus/Types/Game/Library/Targets/Research7Target"
].map(type => ({ TargetType: type, Scans: 10, Completed: true }));
}
return inventoryResponse;
};

View File

@ -0,0 +1,20 @@
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts";
export const unlockAllSimarisResearchEntriesController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "LibraryPersonalTarget LibraryPersonalProgress");
inventory.LibraryPersonalTarget = undefined;
inventory.LibraryPersonalProgress = [
"/Lotus/Types/Game/Library/Targets/Research1Target",
"/Lotus/Types/Game/Library/Targets/Research2Target",
"/Lotus/Types/Game/Library/Targets/Research3Target",
"/Lotus/Types/Game/Library/Targets/Research4Target",
"/Lotus/Types/Game/Library/Targets/Research5Target",
"/Lotus/Types/Game/Library/Targets/Research6Target",
"/Lotus/Types/Game/Library/Targets/Research7Target"
].map(type => ({ TargetType: type, Scans: 10, Completed: true }));
await inventory.save();
res.end();
};

View File

@ -15,6 +15,7 @@ import { webuiFileChangeDetectedController } from "../controllers/custom/webuiFi
import { completeAllMissionsController } from "../controllers/custom/completeAllMissionsController.ts";
import { addMissingHelminthBlueprintsController } from "../controllers/custom/addMissingHelminthBlueprintsController.ts";
import { unlockAllProfitTakerStagesController } from "../controllers/custom/unlockAllProfitTakerStagesController.ts";
import { unlockAllSimarisResearchEntriesController } from "../controllers/custom/unlockAllSimarisResearchEntriesController.ts";
import { abilityOverrideController } from "../controllers/custom/abilityOverrideController.ts";
import { createAccountController } from "../controllers/custom/createAccountController.ts";
@ -50,6 +51,7 @@ customRouter.get("/webuiFileChangeDetected", webuiFileChangeDetectedController);
customRouter.get("/completeAllMissions", completeAllMissionsController);
customRouter.get("/addMissingHelminthBlueprints", addMissingHelminthBlueprintsController);
customRouter.get("/unlockAllProfitTakerStages", unlockAllProfitTakerStagesController);
customRouter.get("/unlockAllSimarisResearchEntries", unlockAllSimarisResearchEntriesController);
customRouter.post("/abilityOverride", abilityOverrideController);
customRouter.post("/createAccount", createAccountController);

View File

@ -4,7 +4,7 @@ import { repoDir } from "../helpers/pathHelper.ts";
import { args } from "../helpers/commandLineArguments.ts";
import { Inbox } from "../models/inboxModel.ts";
export interface IConfig extends IConfigRemovedOptions {
export interface IConfig {
mongodbUrl: string;
logger: {
files: boolean;
@ -33,7 +33,6 @@ export interface IConfig extends IConfigRemovedOptions {
noDojoResearchCosts?: boolean;
noDojoResearchTime?: boolean;
fastClanAscension?: boolean;
unlockAllSimarisResearchEntries?: boolean;
spoofMasteryRank?: number;
relicRewardItemCountMultiplier?: number;
nightwaveStandingMultiplier?: number;
@ -105,6 +104,7 @@ export const configRemovedOptionsKeys = [
"unlockExilusEverywhere",
"unlockArcanesEverywhere",
"unlockAllProfitTakerStages",
"unlockAllSimarisResearchEntries",
"noDailyStandingLimits",
"noDailyFocusLimit",
"noArgonCrystalDecay",
@ -123,11 +123,7 @@ export const configRemovedOptionsKeys = [
"flawlessRelicsAlwaysGiveSilverReward",
"radiantRelicsAlwaysGiveGoldReward",
"disableDailyTribute"
] as const;
type IConfigRemovedOptions = {
[K in (typeof configRemovedOptionsKeys)[number]]?: boolean;
};
];
export const configPath = path.join(repoDir, args.configPath ?? "config.json");

View File

@ -1,6 +1,13 @@
import chokidar from "chokidar";
import { logger } from "../utils/logger.ts";
import { config, configPath, configRemovedOptionsKeys, loadConfig, syncConfigWithDatabase } from "./configService.ts";
import {
config,
configPath,
configRemovedOptionsKeys,
loadConfig,
syncConfigWithDatabase,
type IConfig
} from "./configService.ts";
import { saveConfig, shouldReloadConfig } from "./configWriterService.ts";
import { getWebPorts, startWebServer, stopWebServer } from "./webService.ts";
import { sendWsBroadcast } from "./wsService.ts";
@ -35,9 +42,11 @@ chokidar.watch(configPath).on("change", () => {
export const validateConfig = (): void => {
let modified = false;
for (const key of configRemovedOptionsKeys) {
if (config[key] !== undefined) {
logger.debug(`Spotted removed option ${key} with value ${config[key]} in config.json.`);
delete config[key];
if (config[key as keyof IConfig] !== undefined) {
logger.debug(
`Spotted removed option ${key} with value ${String(config[key as keyof IConfig])} in config.json.`
);
delete config[key as keyof IConfig];
modified = true;
}
}

View File

@ -689,6 +689,7 @@ export const addItem = async (
// Path-based duck typing
switch (typeName.substr(1).split("/")[1]) {
case "Powersuits":
if (typeName.endsWith("AugmentCard")) break;
switch (typeName.substr(1).split("/")[2]) {
default: {
return {
@ -773,6 +774,10 @@ export const addItem = async (
}
}
break;
case "Skins": {
return addSkin(inventory, typeName);
}
}
break;
}
@ -869,6 +874,26 @@ export const addItem = async (
break;
}
break;
case "Weapons": {
if (typeName.substr(1).split("/")[4] == "MeleeTrees") break;
const productCategory = typeName.substr(1).split("/")[3];
switch (productCategory) {
case "Pistols":
case "LongGuns":
case "Melee": {
const inventoryChanges = addEquipment(inventory, productCategory, typeName);
return {
...inventoryChanges,
...occupySlot(
inventory,
productCategoryToInventoryBin(productCategory) ?? InventorySlot.WEAPONS,
premiumPurchase
)
};
}
}
break;
}
}
throw new Error(`unable to add item: ${typeName}`);
};

View File

@ -1481,7 +1481,7 @@ export const addMissionRewards = async (
if (vault) {
currentJob = vault;
if (jobType.endsWith("VaultBounty")) {
currentJob.xpAmounts = [currentJob.xpAmounts.reduce((partialSum, a) => partialSum + a, 0)];
currentJob.xpAmounts[rewardInfo.JobTier!] = currentJob.xpAmounts.reduce((s, a) => s + a, 0);
}
}
}
@ -1498,15 +1498,22 @@ export const addMissionRewards = async (
medallionAmount = Math.floor(endlessJob.xpAmounts[index] * (1 + 0.15000001 * excess));
}
}
await addItem(inventory, "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB", medallionAmount);
MissionRewards.push({
StoreItem: "/Lotus/StoreItems/Types/Items/Deimos/EntratiFragmentUncommonB",
ItemCount: medallionAmount
});
SyndicateXPItemReward = medallionAmount;
logger.debug(
`Giving ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
);
if (typeof medallionAmount === "number" && !isNaN(medallionAmount)) {
await addItem(inventory, "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB", medallionAmount);
MissionRewards.push({
StoreItem: "/Lotus/StoreItems/Types/Items/Deimos/EntratiFragmentUncommonB",
ItemCount: medallionAmount
});
SyndicateXPItemReward = medallionAmount;
logger.debug(
`Giving ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
);
} else {
logger.warning(
`${jobType} tried to give ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
);
logger.warning(`currentJob`, { currentJob: currentJob });
}
} else {
const specialCase = [
{ endings: ["Heists/HeistProfitTakerBountyOne"], stage: 2, amount: 1000 },

View File

@ -92,12 +92,36 @@
<div data-route="/webui/inventory" data-title="Inventory | OpenWF WebUI">
<p class="mb-3" data-loc="general_inventoryUpdateNote"></p>
<div class="card mb-3">
<h5 class="card-header" data-loc="inventory_addItems"></h5>
<form class="card-body input-group" onsubmit="doAcquireMiscItems();return false;">
<input class="form-control" id="miscitem-count" type="number" value="1" />
<input class="form-control w-50" id="miscitem-type" list="datalist-miscitems" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form>
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<button class="nav-link" id="miscItems-tab" data-bs-toggle="tab" data-bs-target="#miscItems-tab-content" data-loc="inventory_addItems"></button>
</li>
<li class="nav-item">
<button class="nav-link" id="typeName-tab" data-bs-toggle="tab" data-bs-target="#typeName-tab-content" data-loc="inventory_addItemByItemType"></button>
</li>
</ul>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane" id="miscItems-tab-content">
<form class="card-body input-group" onsubmit="doAcquireMiscItems();return false;">
<input class="form-control" id="miscitem-count" type="number" value="1" />
<input class="form-control w-50" id="miscitem-type" list="datalist-miscitems" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form>
</div>
<div class="tab-pane" id="typeName-tab-content">
<form class="card-body" onsubmit="addItemByItemType();return false;">
<p data-loc="inventory_addItemByItemType_warning"></p>
<div class="input-group">
<input class="form-control" id="typeName-type" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-3">
@ -775,10 +799,6 @@
<input class="form-check-input" type="checkbox" id="radiantRelicsAlwaysGiveGoldReward" />
<label class="form-check-label" for="radiantRelicsAlwaysGiveGoldReward" data-loc="cheats_radiantRelicsAlwaysGiveGoldReward"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllSimarisResearchEntries" />
<label class="form-check-label" for="unlockAllSimarisResearchEntries" data-loc="cheats_unlockAllSimarisResearchEntries"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="disableDailyTribute" />
<label class="form-check-label" for="disableDailyTribute" data-loc="cheats_disableDailyTribute"></label>
@ -788,14 +808,15 @@
<label class="form-check-label" for="finishInvasionsInOneMission" data-loc="cheats_finishInvasionsInOneMission"></label>
</div>
<div class="mt-2 mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="debounce(doUnlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
<button class="btn btn-primary" onclick="debounce(unlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
<button class="btn btn-primary" onclick="debounce(markAllAsRead);" data-loc="cheats_markAllAsRead"></button>
<button class="btn btn-primary" onclick="doUnlockAllFocusSchools();" data-loc="cheats_unlockAllFocusSchools"></button>
<button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
<button class="btn btn-primary" onclick="debounce(addMissingHelminthRecipes);" data-loc="cheats_addMissingSubsumedAbilities"></button>
<button class="btn btn-primary" onclick="doIntrinsicsUnlockAll();" data-loc="cheats_intrinsicsUnlockAll"></button>
<button class="btn btn-primary" onclick="debounce(doMaxPlexus);" data-loc="inventory_maxPlexus"></button>
<button class="btn btn-primary" onclick="debounce(doUnlockAllProfitTakerStages);" data-loc="cheats_unlockAllProfitTakerStages"></button>
<button class="btn btn-primary" onclick="debounce(unlockAllProfitTakerStages);" data-loc="cheats_unlockAllProfitTakerStages"></button>
<button class="btn btn-primary" onclick="debounce(unlockAllSimarisResearchEntries);" data-loc="cheats_unlockAllSimarisResearchEntries"></button>
</div>
<form class="mt-2" onsubmit="doChangeSupportedSyndicate(); return false;">
<label class="form-label" for="changeSyndicate" data-loc="cheats_changeSupportedSyndicate"></label>

View File

@ -649,6 +649,14 @@ function updateInventory() {
];
// Populate inventory route
document.getElementById("typeName-tab").classList.remove("active");
document.getElementById("typeName-tab-content").classList.remove("active", "show");
document.getElementById("typeName-type").value = "";
document.getElementById("miscItems-tab").classList.add("active");
document.getElementById("miscItems-tab-content").classList.add("active", "show");
["RegularCredits", "PremiumCredits", "FusionPoints", "PrimeTokens"].forEach(currency => {
document.getElementById(currency + "-owned").textContent = loc("currency_owned")
.split("|COUNT|")
@ -1685,7 +1693,7 @@ function doAcquireEvolution() {
setEvolutionProgress([{ ItemType: uniqueName, Rank: permanentEvolutionWeapons.has(uniqueName) ? 0 : 1 }]);
}
$(document).on("input", "input[list]", function () {
$(document).on("input", "input", function () {
$(this).removeClass("is-invalid");
});
@ -2008,6 +2016,35 @@ function doAcquireMiscItems() {
}
}
function addItemByItemType() {
const ItemType = document.getElementById("typeName-type").value;
// Must start with "/Lotus/", contain only AZ letters, no "//", and not end with "/"
if (!ItemType || !/^\/Lotus\/(?:[A-Za-z]+(?:\/[A-Za-z]+)*)$/.test(ItemType)) {
$("#typeName-type").addClass("is-invalid").focus();
return;
}
revalidateAuthz().then(() => {
$.post({
url: "/custom/addItems?" + window.authz,
contentType: "application/json",
data: JSON.stringify([
{
ItemType,
ItemCount: 1
}
])
})
.done(function (_, _, jqXHR) {
if (jqXHR.status === 200) {
updateInventory();
}
})
.fail(function () {
$("#typeName-type").addClass("is-invalid").focus();
});
});
}
function doAcquireRiven() {
let fingerprint;
try {
@ -2713,18 +2750,24 @@ async function doMaxPlexus() {
}
}
async function doUnlockAllMissions() {
async function unlockAllMissions() {
await revalidateAuthz();
await fetch("/custom/completeAllMissions?" + window.authz);
toast(loc("cheats_unlockAllMissions_ok"));
}
async function doUnlockAllProfitTakerStages() {
async function unlockAllProfitTakerStages() {
await revalidateAuthz();
await fetch("/custom/unlockAllProfitTakerStages?" + window.authz);
toast(loc("cheats_unlockSucc"));
}
async function unlockAllSimarisResearchEntries() {
await revalidateAuthz();
await fetch("/custom/unlockAllSimarisResearchEntries?" + window.authz);
toast(loc("cheats_unlockSucc"));
}
const importSamples = {
maxFocus: {
FocusUpgrades: [

View File

@ -79,6 +79,8 @@ dict = {
navbar_cheats: `Cheats`,
navbar_import: `Importieren`,
inventory_addItems: `Gegenstände hinzufügen`,
inventory_addItemByItemType: `[UNTRANSLATED] Raw`,
inventory_addItemByItemType_warning: `[UNTRANSLATED] Use this feature at your own risk. It may break your inventory, and you will need to remove items manually if something goes wrong.`,
inventory_suits: `Warframes`,
inventory_longGuns: `Primärwaffen`,
inventory_pistols: `Sekundärwaffen`,

View File

@ -78,6 +78,8 @@ dict = {
navbar_cheats: `Cheats`,
navbar_import: `Import`,
inventory_addItems: `Add Items`,
inventory_addItemByItemType: `Raw`,
inventory_addItemByItemType_warning: `Use this feature at your own risk. It may break your inventory, and you will need to remove items manually if something goes wrong.`,
inventory_suits: `Warframes`,
inventory_longGuns: `Primary Weapons`,
inventory_pistols: `Secondary Weapons`,

View File

@ -79,6 +79,8 @@ dict = {
navbar_cheats: `Trucos`,
navbar_import: `Importar`,
inventory_addItems: `Agregar objetos`,
inventory_addItemByItemType: `[UNTRANSLATED] Raw`,
inventory_addItemByItemType_warning: `[UNTRANSLATED] Use this feature at your own risk. It may break your inventory, and you will need to remove items manually if something goes wrong.`,
inventory_suits: `Warframes`,
inventory_longGuns: `Armas primarias`,
inventory_pistols: `Armas secundarias`,

View File

@ -79,6 +79,8 @@ dict = {
navbar_cheats: `Cheats`,
navbar_import: `Importer`,
inventory_addItems: `Ajouter des items`,
inventory_addItemByItemType: `[UNTRANSLATED] Raw`,
inventory_addItemByItemType_warning: `[UNTRANSLATED] Use this feature at your own risk. It may break your inventory, and you will need to remove items manually if something goes wrong.`,
inventory_suits: `Warframes`,
inventory_longGuns: `Armes principales`,
inventory_pistols: `Armes secondaires`,

View File

@ -79,6 +79,8 @@ dict = {
navbar_cheats: `Читы`,
navbar_import: `Импорт`,
inventory_addItems: `Добавить предметы`,
inventory_addItemByItemType: `[UNTRANSLATED] Raw`,
inventory_addItemByItemType_warning: `Используйте эту функцию на свой страх и риск. Она может повредить ваш инвентарь, и в случае проблем вам придётся удалять предметы вручную.`,
inventory_suits: `Варфреймы`,
inventory_longGuns: `Основное оружие`,
inventory_pistols: `Вторичное оружие`,
@ -210,7 +212,7 @@ dict = {
cheats_baroFullyStocked: `Баро полностью укомплектован`,
cheats_syndicateMissionsRepeatable: `Повторять миссии синдиката`,
cheats_unlockAllProfitTakerStages: `Разблокировать все этапы Сферы извлечения прибыли`,
cheats_unlockSucc: `[UNTRANSLATED] Successfully unlocked.`,
cheats_unlockSucc: `Успешно разблокировано.`,
cheats_instantFinishRivenChallenge: `Мгновенное завершение испытания мода Разлома`,
cheats_instantResourceExtractorDrones: `Мгновенно добывающие Дроны-сборщики`,
cheats_noResourceExtractorDronesDamage: `Без урона по Дронам-сборщикам`,
@ -239,7 +241,7 @@ dict = {
cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
cheats_changeButton: `Изменить`,
cheats_markAllAsRead: `Пометить все входящие как прочитанные`,
cheats_finishInvasionsInOneMission: `[UNTRANSLATED] Finish Invasions in One Mission`,
cheats_finishInvasionsInOneMission: `Завершать вторжение за одну миссию`,
worldState: `Состояние мира`,
worldState_creditBoost: `Глобальный бустер Кредитов`,

View File

@ -79,6 +79,8 @@ dict = {
navbar_cheats: `Чити`,
navbar_import: `Імпорт`,
inventory_addItems: `Додати предмети`,
inventory_addItemByItemType: `[UNTRANSLATED] Raw`,
inventory_addItemByItemType_warning: `[UNTRANSLATED] Use this feature at your own risk. It may break your inventory, and you will need to remove items manually if something goes wrong.`,
inventory_suits: `Ворфрейми`,
inventory_longGuns: `Основна зброя`,
inventory_pistols: `Допоміжна зброя`,
@ -210,7 +212,7 @@ dict = {
cheats_baroFullyStocked: `Баро повністю укомплектований`,
cheats_syndicateMissionsRepeatable: `Повторювати місії синдиката`,
cheats_unlockAllProfitTakerStages: `Розблокувати всі етапи Привласнювачки`,
cheats_unlockSucc: `[UNTRANSLATED] Successfully unlocked.`,
cheats_unlockSucc: `Успішно розблоковано.`,
cheats_instantFinishRivenChallenge: `Миттєве завершення випробування модифікатора Розколу`,
cheats_instantResourceExtractorDrones: `Миттєво добуваючі Дрони-видобувачі`,
cheats_noResourceExtractorDronesDamage: `Без шкоди по Дронам-видобувачам`,
@ -239,7 +241,7 @@ dict = {
cheats_changeSupportedSyndicate: `Підтримуваний синдикат`,
cheats_changeButton: `Змінити`,
cheats_markAllAsRead: `Помітити всі вхідні як прочитані`,
cheats_finishInvasionsInOneMission: `[UNTRANSLATED] Finish Invasions in One Mission`,
cheats_finishInvasionsInOneMission: `Завершувати вторгнення за одну місію`,
worldState: `Стан світу`,
worldState_creditBoost: `Глобальне посилення Кредитів`,

View File

@ -79,6 +79,8 @@ dict = {
navbar_cheats: `作弊选项`,
navbar_import: `导入`,
inventory_addItems: `添加物品`,
inventory_addItemByItemType: `[UNTRANSLATED] Raw`,
inventory_addItemByItemType_warning: `[UNTRANSLATED] Use this feature at your own risk. It may break your inventory, and you will need to remove items manually if something goes wrong.`,
inventory_suits: `战甲`,
inventory_longGuns: `主要武器`,
inventory_pistols: `次要武器`,