fix(webui): quest cheats #965

Merged
OrdisPrime merged 5 commits from fix-completeallunlocked into main 2025-02-19 14:09:47 -08:00
6 changed files with 161 additions and 65 deletions

View File

@ -1,34 +1,19 @@
import { RequestHandler } from "express";
import { isEmptyObject, parseString } from "@/src/helpers/general";
import { parseString } from "@/src/helpers/general";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addKeyChainItems, getInventory } from "@/src/services/inventoryService";
import { getInventory } from "@/src/services/inventoryService";
import { IGroup } from "@/src/types/loginTypes";
import { updateQuestStage } from "@/src/services/questService";
import { giveKeyChainItem } from "@/src/services/questService";
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
const accountId = parseString(req.query.accountId);
const keyChainInfo = getJSONfromString<IKeyChainRequest>((req.body as string).toString());
const inventory = await getInventory(accountId);
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
if (isEmptyObject(inventoryChanges)) {
throw new Error("inventory changes was empty after getting keychain items: should not happen");
}
// items were added: update quest stage's i (item was given)
updateQuestStage(inventory, keyChainInfo, { i: true });
const inventoryChanges = giveKeyChainItem(inventory, keyChainInfo);
await inventory.save();
res.send(inventoryChanges);
//TODO: Check whether Wishlist is used to track items which should exist uniquely in the inventory
/*
some items are added or removed (not sure) to the wishlist, in that case a
WishlistChanges: ["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],
is added to the response, need to determine for which items this is the case and what purpose this has.
*/
//{"KeyChain":"/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain","ChainStage":0}
//{"WishlistChanges":["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],"MiscItems":[{"ItemType":"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem","ItemCount":1}]}
res.send(inventoryChanges);
};
export interface IKeyChainRequest {

View File

@ -1,34 +1,15 @@
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
import { IMessage } from "@/src/models/inboxModel";
import { createMessage } from "@/src/services/inboxService";
import { getInventory } from "@/src/services/inventoryService";
import { getKeyChainMessage } from "@/src/services/itemDataService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { updateQuestStage } from "@/src/services/questService";
import { giveKeyChainMessage } from "@/src/services/questService";
import { RequestHandler } from "express";
export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const keyChainInfo = JSON.parse((req.body as Buffer).toString()) as IKeyChainRequest;
const keyChainMessage = getKeyChainMessage(keyChainInfo);
const message = {
sndr: keyChainMessage.sender,
msg: keyChainMessage.body,
sub: keyChainMessage.title,
att: keyChainMessage.attachments.length > 0 ? keyChainMessage.attachments : undefined,
countedAtt: keyChainMessage.countedAttachments.length > 0 ? keyChainMessage.countedAttachments : undefined,
icon: keyChainMessage.icon ?? "",
transmission: keyChainMessage.transmission ?? "",
highPriority: keyChainMessage.highPriority ?? false,
r: false
} satisfies IMessage;
await createMessage(accountId, [message]);
const inventory = await getInventory(accountId, "QuestKeys");
updateQuestStage(inventory, keyChainInfo, { m: true });
await giveKeyChainMessage(inventory, accountId, keyChainInfo);
await inventory.save();
res.send(1);

View File

@ -1,8 +1,7 @@
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 { addQuestKey, completeQuest, IUpdateQuestRequest, updateQuestKey } from "@/src/services/questService";
import { logger } from "@/src/utils/logger";
import { RequestHandler } from "express";
import { ExportKeys } from "warframe-public-export-plus";
@ -23,7 +22,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
allQuestKeys.push(k);
}
}
const inventory = await getInventory(accountId, "QuestKeys NodeIntrosCompleted");
const inventory = await getInventory(accountId);
switch (operation) {
case "updateKey": {
@ -40,21 +39,31 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
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;
try {
await completeQuest(inventory, questKey);
} catch (error) {
if (error instanceof Error) {
logger.error(
`Something went wrong completing quest ${questKey}, probably could not add some item`
);
logger.error(error.message);
}
}
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");
//Skip "Watch The Maker"
if (questKey === "/Lotus/Types/Keys/NewWarIntroQuest/NewWarIntroKeyChain") {
addString(
inventory.NodeIntrosCompleted,
"/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level"
);
}
if (questKey === "/Lotus/Types/Keys/ArchwingQuest/ArchwingQuestKeyChain") {
inventory.ArchwingEnabled = true;
}
}
inventory.ActiveQuest = "";
break;
}
case "ResetAll": {
@ -63,14 +72,37 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
questKey.Completed = false;
questKey.Progress = [];
}
inventory.ActiveQuest = "";
break;
}
case "completeAllUnlocked": {
logger.info("completing all unlocked quests..");
for (const questKey of inventory.QuestKeys) {
//if (!questKey.unlock) { continue; }
questKey.Completed = true;
console.log("size of questkeys", inventory.QuestKeys.length);
try {
await completeQuest(inventory, questKey.ItemType);
} catch (error) {
if (error instanceof Error) {
logger.error(
`Something went wrong completing quest ${questKey.ItemType}, probably could not add some item`
);
logger.error(error.message);
}
}
//Skip "Watch The Maker"
if (questKey.ItemType === "/Lotus/Types/Keys/NewWarIntroQuest/NewWarIntroKeyChain") {
addString(
inventory.NodeIntrosCompleted,
"/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level"
);
}
if (questKey.ItemType === "/Lotus/Types/Keys/ArchwingQuest/ArchwingQuestKeyChain") {
inventory.ArchwingEnabled = true;
}
}
inventory.ActiveQuest = "";
break;
}
}

View File

@ -1249,6 +1249,7 @@ export type InventoryDocumentProps = {
KahlLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
PendingRecipes: Types.DocumentArray<IPendingRecipeDatabase>;
WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>;
QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
// eslint-disable-next-line @typescript-eslint/ban-types

View File

@ -367,7 +367,7 @@ export const addItem = async (
};
}
if (typeName in ExportKeys) {
// Note: "/Lotus/Types/Keys/" contains some EmailItems and ShipFeatureItems
// Note: "/Lotus/Types/Keys/" contains some EmailItems
inventory.QuestKeys.push({ ItemType: typeName });
return {
InventoryChanges: {
@ -519,9 +519,7 @@ export const addItem = async (
}
break;
}
const errorMessage = `unable to add item: ${typeName}`;
logger.error(errorMessage);
throw new Error(errorMessage);
throw new Error(`unable to add item: ${typeName}`);
};
export const addItems = async (
@ -1156,7 +1154,5 @@ export const addKeyChainItems = async (
combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges);
}
await addItems(inventory, nonStoreItems);
return inventoryChanges;
};

View File

@ -1,8 +1,14 @@
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
import { isEmptyObject } from "@/src/helpers/general";
import { IMessage } from "@/src/models/inboxModel";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { createMessage } from "@/src/services/inboxService";
import { addKeyChainItems } from "@/src/services/inventoryService";
import { getKeyChainMessage } from "@/src/services/itemDataService";
import { IInventoryDatabase, IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes";
import { logger } from "@/src/utils/logger";
import { HydratedDocument } from "mongoose";
import { ExportKeys } from "warframe-public-export-plus";
export interface IUpdateQuestRequest {
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[];
@ -70,3 +76,98 @@ export const addQuestKey = (inventory: TInventoryDatabaseDocument, questKey: IQu
}
inventory.QuestKeys.push(questKey);
};
export const completeQuest = async (inventory: TInventoryDatabaseDocument, questKey: string) => {
const chainStages = ExportKeys[questKey]?.chainStages;
if (!chainStages) {
throw new Error(`Quest ${questKey} does not contain chain stages`);
}
const chainStageTotal = ExportKeys[questKey].chainStages?.length ?? 0;
const existingQuestKey = inventory.QuestKeys.find(qk => qk.ItemType === questKey);
if (existingQuestKey?.Completed) {
return;
}
const Progress = Array(chainStageTotal).fill({
c: 0,
i: false,
m: false,
b: []
} satisfies IQuestStage);
const completedQuestKey: IQuestKeyDatabase = {
ItemType: questKey,
Completed: true,
unlock: true,
Progress: Progress,
CompletionDate: new Date()
};
//overwrite current quest progress, might lead to multiple quest item rewards
if (existingQuestKey) {
existingQuestKey.overwrite(completedQuestKey);
//Object.assign(existingQuestKey, completedQuestKey);
} else {
addQuestKey(inventory, completedQuestKey);
}
for (let i = 0; i < chainStageTotal; i++) {
if (chainStages[i].itemsToGiveWhenTriggered.length > 0) {
await giveKeyChainItem(inventory, { KeyChain: questKey, ChainStage: i });
}
if (chainStages[i].messageToSendWhenTriggered) {
await giveKeyChainMessage(inventory, inventory.accountOwnerId.toString(), {
KeyChain: questKey,
ChainStage: i
});
}
}
//TODO: handle quest completions
};
export const giveKeyChainItem = async (inventory: TInventoryDatabaseDocument, keyChainInfo: IKeyChainRequest) => {
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
if (isEmptyObject(inventoryChanges)) {
throw new Error("inventory changes was empty after getting keychain items: should not happen");
}
// items were added: update quest stage's i (item was given)
updateQuestStage(inventory, keyChainInfo, { i: true });
//TODO: Check whether Wishlist is used to track items which should exist uniquely in the inventory
/*
some items are added or removed (not sure) to the wishlist, in that case a
WishlistChanges: ["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],
is added to the response, need to determine for which items this is the case and what purpose this has.
*/
//{"KeyChain":"/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain","ChainStage":0}
//{"WishlistChanges":["/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem"],"MiscItems":[{"ItemType":"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem","ItemCount":1}]}
};
export const giveKeyChainMessage = async (
inventory: TInventoryDatabaseDocument,
accountId: string,
keyChainInfo: IKeyChainRequest
) => {
const keyChainMessage = getKeyChainMessage(keyChainInfo);
const message = {
sndr: keyChainMessage.sender,
msg: keyChainMessage.body,
sub: keyChainMessage.title,
att: keyChainMessage.attachments.length > 0 ? keyChainMessage.attachments : undefined,
countedAtt: keyChainMessage.countedAttachments.length > 0 ? keyChainMessage.countedAttachments : undefined,
icon: keyChainMessage.icon ?? "",
transmission: keyChainMessage.transmission ?? "",
highPriority: keyChainMessage.highPriority ?? false,
r: false
} satisfies IMessage;
await createMessage(accountId, [message]);
updateQuestStage(inventory, keyChainInfo, { m: true });
};