feat: move quest cheats to webui (#963)
All checks were successful
Build Docker image / docker (push) Successful in 35s
Build / build (22) (push) Successful in 1m9s
Build / build (20) (push) Successful in 36s
Build / build (18) (push) Successful in 57s

Co-authored-by: Sainan <sainan@calamity.inc>
Reviewed-on: #963
Co-authored-by: Ordis <134585663+OrdisPrime@users.noreply.github.com>
Co-committed-by: Ordis <134585663+OrdisPrime@users.noreply.github.com>
This commit is contained in:
Ordis 2025-02-18 17:14:42 -08:00 committed by Sainan
parent 87cc2594c8
commit 1413a6bcc2
10 changed files with 145 additions and 54 deletions

View File

@ -13,8 +13,6 @@
"skipAllDialogue": true, "skipAllDialogue": true,
"unlockAllScans": true, "unlockAllScans": true,
"unlockAllMissions": true, "unlockAllMissions": true,
"unlockAllQuests": true,
"completeAllQuests": true,
"infiniteCredits": true, "infiniteCredits": true,
"infinitePlatinum": true, "infinitePlatinum": true,
"infiniteEndo": true, "infiniteEndo": true,

View File

@ -9,7 +9,6 @@ import { IPolarity, ArtifactPolarity, EquipmentFeatures } from "@/src/types/inve
import { import {
ExportCustoms, ExportCustoms,
ExportFlavour, ExportFlavour,
ExportKeys,
ExportRegions, ExportRegions,
ExportResources, ExportResources,
ExportVirtuals ExportVirtuals
@ -102,42 +101,6 @@ export const getInventoryResponse = async (
addString(inventoryResponse.NodeIntrosCompleted, "TeshinHardModeUnlocked"); addString(inventoryResponse.NodeIntrosCompleted, "TeshinHardModeUnlocked");
} }
if (config.unlockAllQuests) {
for (const [k, v] of Object.entries(ExportKeys)) {
if ("chainStages" in v) {
if (!inventoryResponse.QuestKeys.find(quest => quest.ItemType == k)) {
inventoryResponse.QuestKeys.push({ ItemType: k });
}
}
}
}
if (config.completeAllQuests) {
for (const quest of inventoryResponse.QuestKeys) {
quest.unlock = true;
quest.Completed = true;
let numStages = 1;
if (quest.ItemType in ExportKeys && "chainStages" in ExportKeys[quest.ItemType]) {
numStages = ExportKeys[quest.ItemType].chainStages!.length;
}
quest.Progress = [];
for (let i = 0; i != numStages; ++i) {
quest.Progress.push({
c: 0,
i: false,
m: false,
b: []
});
}
}
inventoryResponse.ArchwingEnabled = true;
inventoryResponse.ActiveQuest = ""; //TODO: might need to reconsider this if this does not work long term.
// Skip "Watch The Maker"
addString(inventoryResponse.NodeIntrosCompleted, "/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level");
}
if (config.unlockAllShipDecorations) { if (config.unlockAllShipDecorations) {
inventoryResponse.ShipDecorations = []; inventoryResponse.ShipDecorations = [];
for (const [uniqueName, item] of Object.entries(ExportResources)) { for (const [uniqueName, item] of Object.entries(ExportResources)) {
@ -261,7 +224,7 @@ export const getInventoryResponse = async (
return inventoryResponse; return inventoryResponse;
}; };
const addString = (arr: string[], str: string): void => { export const addString = (arr: string[], str: string): void => {
if (!arr.find(x => x == str)) { if (!arr.find(x => x == str)) {
arr.push(str); arr.push(str);
} }

View File

@ -0,0 +1,80 @@
import { addString } from "@/src/controllers/api/inventoryController";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addQuestKey, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
import { IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
import { ExportKeys } from "warframe-public-export-plus";
export const manageQuestsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const operation = req.query.operation as
| "unlockAll"
| "completeAll"
| "ResetAll"
| "completeAllUnlocked"
| "updateKey";
const questKeyUpdate = req.body as IUpdateQuestRequest["QuestKeys"];
const allQuestKeys: string[] = [];
for (const [k, v] of Object.entries(ExportKeys)) {
if ("chainStages" in v) {
allQuestKeys.push(k);
}
}
const inventory = await getInventory(accountId, "QuestKeys NodeIntrosCompleted");
switch (operation) {
case "updateKey": {
//TODO: if this is intended to be used, one needs to add a updateQuestKeyMultiple, the game does never intend to do it, so it errors for multiple keys.
updateQuestKey(inventory, questKeyUpdate);
break;
}
case "unlockAll": {
for (const questKey of allQuestKeys) {
addQuestKey(inventory, { ItemType: questKey, Completed: false, unlock: true, Progress: [] });
}
break;
}
case "completeAll": {
logger.info("completing all quests..");
for (const questKey of allQuestKeys) {
const chainStageTotal = ExportKeys[questKey].chainStages?.length ?? 0;
const Progress = Array(chainStageTotal).fill({ c: 0, i: true, m: true, b: [] } satisfies IQuestStage);
const inventoryQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
if (inventoryQuestKey) {
inventoryQuestKey.Completed = true;
inventoryQuestKey.Progress = Progress;
continue;
}
addQuestKey(inventory, { ItemType: questKey, Completed: true, unlock: true, Progress: Progress });
}
inventory.ArchwingEnabled = true;
inventory.ActiveQuest = "";
// Skip "Watch The Maker"
addString(inventory.NodeIntrosCompleted, "/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level");
break;
}
case "ResetAll": {
logger.info("resetting all quests..");
for (const questKey of inventory.QuestKeys) {
questKey.Completed = false;
questKey.Progress = [];
}
break;
}
case "completeAllUnlocked": {
logger.info("completing all unlocked quests..");
for (const questKey of inventory.QuestKeys) {
//if (!questKey.unlock) { continue; }
questKey.Completed = true;
}
break;
}
}
await inventory.save();
res.status(200).end();
};

View File

@ -16,6 +16,7 @@ import { importController } from "@/src/controllers/custom/importController";
import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController"; import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController"; import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
const customRouter = express.Router(); const customRouter = express.Router();
@ -32,6 +33,7 @@ customRouter.post("/addCurrency", addCurrencyController);
customRouter.post("/addItems", addItemsController); customRouter.post("/addItems", addItemsController);
customRouter.post("/addXp", addXpController); customRouter.post("/addXp", addXpController);
customRouter.post("/import", importController); customRouter.post("/import", importController);
customRouter.post("/manageQuests", manageQuestsController);
customRouter.get("/config", getConfigDataController); customRouter.get("/config", getConfigDataController);
customRouter.post("/config", updateConfigDataController); customRouter.post("/config", updateConfigDataController);

View File

@ -39,8 +39,6 @@ interface IConfig {
skipAllDialogue?: boolean; skipAllDialogue?: boolean;
unlockAllScans?: boolean; unlockAllScans?: boolean;
unlockAllMissions?: boolean; unlockAllMissions?: boolean;
unlockAllQuests?: boolean;
completeAllQuests?: boolean;
infiniteCredits?: boolean; infiniteCredits?: boolean;
infinitePlatinum?: boolean; infinitePlatinum?: boolean;
infiniteEndo?: boolean; infiniteEndo?: boolean;

View File

@ -3,7 +3,6 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento
import { IInventoryDatabase, IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes"; import { IInventoryDatabase, IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { HydratedDocument } from "mongoose"; import { HydratedDocument } from "mongoose";
import { config } from "@/src/services/configService";
export interface IUpdateQuestRequest { export interface IUpdateQuestRequest {
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[]; QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[];
@ -23,10 +22,10 @@ export const updateQuestKey = (
throw new Error("more than 1 quest key not supported"); throw new Error("more than 1 quest key not supported");
} }
let questKeyIndex = inventory.QuestKeys.findIndex(questKey => questKey.ItemType === questKeyUpdate[0].ItemType); const questKeyIndex = inventory.QuestKeys.findIndex(questKey => questKey.ItemType === questKeyUpdate[0].ItemType);
if (questKeyIndex === -1) { if (questKeyIndex === -1) {
if (!config.unlockAllQuests) throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`); throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`);
questKeyIndex = inventory.QuestKeys.push({ ItemType: questKeyUpdate[0].ItemType }) - 1;
} }
inventory.QuestKeys[questKeyIndex] = questKeyUpdate[0]; inventory.QuestKeys[questKeyIndex] = questKeyUpdate[0];
@ -63,3 +62,11 @@ export const updateQuestStage = (
Object.assign(questStage, questStageUpdate); Object.assign(questStage, questStageUpdate);
}; };
export const addQuestKey = (inventory: TInventoryDatabaseDocument, questKey: IQuestKeyDatabase): void => {
if (inventory.QuestKeys.some(q => q.ItemType === questKey.ItemType)) {
logger.error(`quest key ${questKey.ItemType} already exists`);
return;
}
inventory.QuestKeys.push(questKey);
};

View File

@ -410,6 +410,27 @@
</div> </div>
</div> </div>
</div> </div>
<div data-route="/webui/quests" data-title="Quests | OpenWF WebUI">
<div class="card mb-3">
<h5 class="card-header" data-loc="quests_list"></h5>
<div class="card-body">
<table class="table table-hover w-100">
<tbody id="active-quests"></tbody>
</table>
</div>
</div>
<div class="card mb-3">
<h5 class="card-header" data-loc="quests_Actions"></h5>
<div class="card-body">
<div class="mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="doQuestUpdate('unlockAll');" data-loc="quests_UnlockAll"></button>
<button class="btn btn-primary" onclick="doQuestUpdate('completeAll');" data-loc="quests_CompleteAll"></button>
<button class="btn btn-primary" onclick="doQuestUpdate('completeAllUnlocked');" data-loc="quests_CompleteAllUnlocked"></button>
<button class="btn btn-primary" onclick="doQuestUpdate('ResetAll');" data-loc="quests_ResetAll"></button>
</div>
</div>
</div>
</div>
<div data-route="/webui/cheats, /webui/settings" data-title="Cheats | OpenWF WebUI"> <div data-route="/webui/cheats, /webui/settings" data-title="Cheats | OpenWF WebUI">
<div class="row g-3"> <div class="row g-3">
<div class="col-md-6"> <div class="col-md-6">
@ -436,14 +457,6 @@
<input class="form-check-input" type="checkbox" id="unlockAllMissions" /> <input class="form-check-input" type="checkbox" id="unlockAllMissions" />
<label class="form-check-label" for="unlockAllMissions" data-loc="cheats_unlockAllMissions"></label> <label class="form-check-label" for="unlockAllMissions" data-loc="cheats_unlockAllMissions"></label>
</div> </div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllQuests" />
<label class="form-check-label" for="unlockAllQuests" data-loc="cheats_unlockAllQuests"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="completeAllQuests" />
<label class="form-check-label" for="completeAllQuests" data-loc="cheats_completeAllQuests"></label>
</div>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="infiniteCredits" /> <input class="form-check-input" type="checkbox" id="infiniteCredits" />
<label class="form-check-label" for="infiniteCredits" data-loc="cheats_infiniteCredits"></label> <label class="form-check-label" for="infiniteCredits" data-loc="cheats_infiniteCredits"></label>
@ -524,6 +537,13 @@
<button class="btn btn-primary" type="submit" data-loc="cheats_changeButton"></button> <button class="btn btn-primary" type="submit" data-loc="cheats_changeButton"></button>
</div> </div>
</form> </form>
<h5 class="mt-3" data-loc="cheats_quests"></h6>
<div class="mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="doQuestUpdate('unlockAll');" data-loc="cheats_quests_UnlockAll"></button>
<button class="btn btn-primary" onclick="doQuestUpdate('completeAll');" data-loc="cheats_quests_CompleteAll"></button>
<button class="btn btn-primary" onclick="doQuestUpdate('completeAllUnlocked');" data-loc="cheats_quests_CompleteAllUnlocked"></button>
<button class="btn btn-primary" onclick="doQuestUpdate('ResetAll');" data-loc="cheats_quests_ResetAll"></button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1153,3 +1153,12 @@ function doAddCurrency(currency) {
updateInventory(); updateInventory();
}); });
} }
function doQuestUpdate(operation) {
$.post({
url: "/custom/manageQuests?" + window.authz + "&operation=" + operation,
contentType: "application/json"
}).then(function () {
updateInventory();
});
}

View File

@ -45,6 +45,7 @@ dict = {
navbar_deleteAccount: `Delete Account`, navbar_deleteAccount: `Delete Account`,
navbar_inventory: `Inventory`, navbar_inventory: `Inventory`,
navbar_mods: `Mods`, navbar_mods: `Mods`,
navbar_quests: `Quests`,
navbar_cheats: `Cheats`, navbar_cheats: `Cheats`,
navbar_import: `Import`, navbar_import: `Import`,
inventory_addItems: `Add Items`, inventory_addItems: `Add Items`,
@ -72,6 +73,7 @@ dict = {
inventory_bulkRankUpSpaceWeapons: `Max Rank All Archwing Weapons`, inventory_bulkRankUpSpaceWeapons: `Max Rank All Archwing Weapons`,
inventory_bulkRankUpSentinels: `Max Rank All Sentinels`, inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`, inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
currency_RegularCredits: `Credits`, currency_RegularCredits: `Credits`,
currency_PremiumCredits: `Platinum`, currency_PremiumCredits: `Platinum`,
currency_FusionPoints: `Endo`, currency_FusionPoints: `Endo`,
@ -115,6 +117,11 @@ dict = {
cheats_changeSupportedSyndicate: `Supported syndicate`, cheats_changeSupportedSyndicate: `Supported syndicate`,
cheats_changeButton: `Change`, cheats_changeButton: `Change`,
cheats_none: `None`, cheats_none: `None`,
cheats_quests: `Quests`,
cheats_quests_UnlockAll: `Unlock All Quests`,
cheats_quests_CompleteAll: `Complete All Quests`,
cheats_quests_CompleteAllUnlocked: `Complete All Unlocked Quests`,
cheats_quests_ResetAll: `Reset All Quests`,
import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer <b>will be overwritten</b> in your account.`, import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer <b>will be overwritten</b> in your account.`,
import_submit: `Submit` import_submit: `Submit`
}; };

View File

@ -46,6 +46,7 @@ dict = {
navbar_deleteAccount: `Удалить аккаунт`, navbar_deleteAccount: `Удалить аккаунт`,
navbar_inventory: `Инвентарь`, navbar_inventory: `Инвентарь`,
navbar_mods: `Моды`, navbar_mods: `Моды`,
navbar_quests: `[UNTRANSLATED] Quests`,
navbar_cheats: `Читы`, navbar_cheats: `Читы`,
navbar_import: `Импорт`, navbar_import: `Импорт`,
inventory_addItems: `Добавить предметы`, inventory_addItems: `Добавить предметы`,
@ -73,6 +74,7 @@ dict = {
inventory_bulkRankUpSpaceWeapons: `Максимальный ранг всего оружия арчвингов`, inventory_bulkRankUpSpaceWeapons: `Максимальный ранг всего оружия арчвингов`,
inventory_bulkRankUpSentinels: `Максимальный ранг всех стражей`, inventory_bulkRankUpSentinels: `Максимальный ранг всех стражей`,
inventory_bulkRankUpSentinelWeapons: `Максимальный ранг всего оружия стражей`, inventory_bulkRankUpSentinelWeapons: `Максимальный ранг всего оружия стражей`,
currency_RegularCredits: `Кредиты`, currency_RegularCredits: `Кредиты`,
currency_PremiumCredits: `Платина`, currency_PremiumCredits: `Платина`,
currency_FusionPoints: `Эндо`, currency_FusionPoints: `Эндо`,
@ -116,6 +118,11 @@ dict = {
cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`, cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
cheats_changeButton: `Изменить`, cheats_changeButton: `Изменить`,
cheats_none: `Отсутствует`, cheats_none: `Отсутствует`,
cheats_quests: `[UNTRANSLATED] Quests`,
cheats_quests_UnlockAll: `[UNTRANSLATED] Unlock All Quests`,
cheats_quests_CompleteAll: `[UNTRANSLATED] Complete All Quests`,
cheats_quests_CompleteAllUnlocked: `[UNTRANSLATED] Complete All Unlocked Quests`,
cheats_quests_ResetAll: `[UNTRANSLATED] Reset All Quests`,
import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля <b>будут перезаписаны</b> в вашем аккаунте.`, import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля <b>будут перезаписаны</b> в вашем аккаунте.`,
import_submit: `Submit` import_submit: `Submit`
}; };