feat: implement awakening quest completion #975
@ -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,
|
||||||
|
@ -1,34 +1,19 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { isEmptyObject, parseString } from "@/src/helpers/general";
|
import { parseString } from "@/src/helpers/general";
|
||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
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 { IGroup } from "@/src/types/loginTypes";
|
||||||
import { updateQuestStage } from "@/src/services/questService";
|
import { giveKeyChainItem } from "@/src/services/questService";
|
||||||
|
|
||||||
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
|
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
|
||||||
const accountId = parseString(req.query.accountId);
|
const accountId = parseString(req.query.accountId);
|
||||||
const keyChainInfo = getJSONfromString<IKeyChainRequest>((req.body as string).toString());
|
const keyChainInfo = getJSONfromString<IKeyChainRequest>((req.body as string).toString());
|
||||||
|
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
|
const inventoryChanges = giveKeyChainItem(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 });
|
|
||||||
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.send(inventoryChanges);
|
|
||||||
|
|
||||||
//TODO: Check whether Wishlist is used to track items which should exist uniquely in the inventory
|
res.send(inventoryChanges);
|
||||||
/*
|
|
||||||
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 interface IKeyChainRequest {
|
export interface IKeyChainRequest {
|
||||||
|
@ -1,34 +1,15 @@
|
|||||||
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
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 { getInventory } from "@/src/services/inventoryService";
|
||||||
import { getKeyChainMessage } from "@/src/services/itemDataService";
|
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { updateQuestStage } from "@/src/services/questService";
|
import { giveKeyChainMessage } from "@/src/services/questService";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {
|
export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const keyChainInfo = JSON.parse((req.body as Buffer).toString()) as IKeyChainRequest;
|
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");
|
const inventory = await getInventory(accountId, "QuestKeys");
|
||||||
updateQuestStage(inventory, keyChainInfo, { m: true });
|
await giveKeyChainMessage(inventory, accountId, keyChainInfo);
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
|
|
||||||
res.send(1);
|
res.send(1);
|
||||||
|
207
src/controllers/api/giveStartingGear.ts
Normal file
207
src/controllers/api/giveStartingGear.ts
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
import { config } from "@/src/services/configService";
|
||||||
|
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
|
import { IOid } from "@/src/types/commonTypes";
|
||||||
|
import { Types } from "mongoose";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
|
import {
|
||||||
|
IDailyAffiliations,
|
||||||
|
IInventoryClient,
|
||||||
|
IPlayerSkills,
|
||||||
|
ITypeCount
|
||||||
|
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { addConsumables, addKeyChainItems, addMods, getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
|
||||||
|
export const giveStartingGearController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId);
|
||||||
|
|
||||||
|
const dataJSON = getJSONfromString<IStartingGearClient>(String(req.body));
|
||||||
|
|
||||||
|
await addKeyChainItems(inventory, {
|
||||||
|
KeyChain: "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain",
|
||||||
|
ChainStage: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const key of Object.keys(dataJSON) as Array<keyof IStartingGearClient>) {
|
||||||
|
switch (key) {
|
||||||
|
// This is the only gear we receive as the rest will come from future quests
|
||||||
|
case "LongGuns":
|
||||||
|
case "Pistols":
|
||||||
|
case "Melee":
|
||||||
|
case "Suits":
|
||||||
|
// Filter out already owned items (shouldnt happen but this was mostly for testing)
|
||||||
|
const existingItems = new Set(inventory[key].map(item => item.ItemType));
|
||||||
|
|
||||||
|
// Adding items to inventory
|
||||||
|
inventory[key].push(
|
||||||
|
...dataJSON[key]
|
||||||
|
.filter(item => !existingItems.has(item.ItemType))
|
||||||
|
.map(x => ({
|
||||||
|
_id: new Types.ObjectId(),
|
||||||
|
ItemType: x.ItemType,
|
||||||
|
XP: x.XP,
|
||||||
|
Configs: [{}, {}, {}]
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addConsumables(inventory, [{ ItemCount: 1, ItemType: "/Lotus/Types/Restoratives/LisetAutoHack" }]);
|
||||||
|
if (!config.unlockAllFlavourItems) {
|
||||||
|
inventory.FlavourItems.push([
|
||||||
|
{ ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem1" },
|
||||||
|
{ ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2" },
|
||||||
|
{ ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3" },
|
||||||
|
{ ItemType: "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4" }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
addMods(inventory, [
|
||||||
|
{
|
||||||
|
ItemCount: 1,
|
||||||
|
ItemType: "/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod",
|
||||||
|
LastAdded: toOid(new Types.ObjectId())
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
inventory.DrifterMelee.push({
|
||||||
|
ItemType: "/Lotus/Types/Friendly/PlayerControllable/Weapons/DuviriDualSwords",
|
||||||
|
_id: new Types.ObjectId()
|
||||||
|
});
|
||||||
|
inventory.PlayedParkourTutorial = true;
|
||||||
|
inventory.ReceivedStartingGear = true;
|
||||||
|
inventory.TrainingDate = new Date();
|
||||||
|
inventory.QuestKeys.push({
|
||||||
|
Progress: [{ i: true }],
|
||||||
|
unlock: false,
|
||||||
|
ItemType: "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"
|
||||||
|
});
|
||||||
|
await inventory.save();
|
||||||
|
|
||||||
|
res.json({});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Alot of stuff is not received in the request, instead of being redundant I will just omit an already created type (pain)
|
||||||
|
interface IStartingGearClient
|
||||||
|
extends Omit<
|
||||||
|
IInventoryClient,
|
||||||
|
| keyof IDailyAffiliations
|
||||||
|
| "Missions"
|
||||||
|
| "RandomUpgradesIdentified"
|
||||||
|
| "LastRegionPlayed"
|
||||||
|
| "TradesRemaining"
|
||||||
|
| "DailyFocus"
|
||||||
|
| "GiftsRemaining"
|
||||||
|
| "HasOwnedVoidProjectionsPreviously"
|
||||||
|
| "ChallengesFixVersion"
|
||||||
|
| "ChallengeProgress"
|
||||||
|
| "ReceivedStartingGear"
|
||||||
|
| "PendingRecipes"
|
||||||
|
| "PendingTrades"
|
||||||
|
| "DeathMarks"
|
||||||
|
| "WebFlags"
|
||||||
|
| "CompletedAlerts"
|
||||||
|
| "TauntHistory"
|
||||||
|
| "StoryModeChoice"
|
||||||
|
| "PeriodicMissionCompletions"
|
||||||
|
| "ActiveDojoColorResearch"
|
||||||
|
| "SentientSpawnChanceBoosters"
|
||||||
|
| "SupportedSyndicate"
|
||||||
|
| "Affiliations"
|
||||||
|
| "QualifyingInvasions"
|
||||||
|
| "FactionScores"
|
||||||
|
| "ArchwingEnabled"
|
||||||
|
| "PendingSpectreLoadouts"
|
||||||
|
| "SpectreLoadouts"
|
||||||
|
| "CompletedSyndicates"
|
||||||
|
| "FocusXP"
|
||||||
|
| "Alignment"
|
||||||
|
| "CompletedSorties"
|
||||||
|
| "LastSortieReward"
|
||||||
|
| "ActiveAvatarImageType"
|
||||||
|
| "DiscoveredMarkers"
|
||||||
|
| "CompletedJobs"
|
||||||
|
| "FocusAbility"
|
||||||
|
| "HasContributedToDojo"
|
||||||
|
| "HWIDProtectEnabled"
|
||||||
|
| "AlignmentReplay"
|
||||||
|
| "PersonalGoalProgress"
|
||||||
|
| "ThemeStyle"
|
||||||
|
| "ThemeBackground"
|
||||||
|
| "ThemeSounds"
|
||||||
|
| "BountyScore"
|
||||||
|
| "ChallengeInstanceStates"
|
||||||
|
| "LoginMilestoneRewards"
|
||||||
|
| "NodeIntrosCompleted"
|
||||||
|
| "GuildId"
|
||||||
|
| "CompletedJobChains"
|
||||||
|
| "SeasonChallengeHistory"
|
||||||
|
| "EquippedInstrument"
|
||||||
|
| "InvasionChainProgress"
|
||||||
|
| "NemesisHistory"
|
||||||
|
| "LastNemesisAllySpawnTime"
|
||||||
|
| "Settings"
|
||||||
|
| "PersonalTechProjects"
|
||||||
|
| "PlayerSkills"
|
||||||
|
| "TradeBannedUntil"
|
||||||
|
| "PlayedParkourTutorial"
|
||||||
|
| "SubscribedToEmailsPersonalized"
|
||||||
|
| "BlessingCooldown"
|
||||||
|
| "NemesisAbandonedRewards"
|
||||||
|
| "LastInventorySync"
|
||||||
|
| "NextRefill"
|
||||||
|
| "CustomMarkers"
|
||||||
|
| "ActiveLandscapeTraps"
|
||||||
|
| "EvolutionProgress"
|
||||||
|
| "RepVotes"
|
||||||
|
| "UsedDailyDeals"
|
||||||
|
| "LibraryPersonalTarget"
|
||||||
|
| "LibraryPersonalProgress"
|
||||||
|
| "CollectibleSeries"
|
||||||
|
| "LibraryAvailableDailyTaskInfo"
|
||||||
|
| "HasResetAccount"
|
||||||
|
| "PendingCoupon"
|
||||||
|
| "Harvestable"
|
||||||
|
| "DeathSquadable"
|
||||||
|
| "EndlessXP"
|
||||||
|
| "DialogueHistory"
|
||||||
|
> {
|
||||||
|
LongGuns: IStartingGearItem[];
|
||||||
|
Melee: IStartingGearItem[];
|
||||||
|
Pistols: IStartingGearItem[];
|
||||||
|
Suits: IStartingGearItem[];
|
||||||
|
XPLost?: unknown[];
|
||||||
|
CrewShipFusionPoints?: number;
|
||||||
|
PlayerSkillGains?: IPlayerSkills[];
|
||||||
|
// Lot's of unknown but these never receive data (at least not in this request)
|
||||||
|
StrippedItems?: unknown[];
|
||||||
|
BonusMiscItems?: unknown[];
|
||||||
|
EmailItems: ITypeCount[];
|
||||||
|
OneTimePurchases?: unknown[];
|
||||||
|
Rating?: number;
|
||||||
|
WishlistChanges?: unknown[];
|
||||||
|
RecentVendorPurchases: (string | number)[];
|
||||||
|
RemovedIdItems?: { ItemId: number }[];
|
||||||
|
SongChallenges?: unknown[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IStartingGearItem
|
||||||
|
extends Omit<
|
||||||
|
IEquipmentClient,
|
||||||
|
| "_id"
|
||||||
|
| "InfestationDate"
|
||||||
|
| "InfestationDays"
|
||||||
|
| "InfestationType"
|
||||||
|
| "UnlockLevel"
|
||||||
|
| "Weapon"
|
||||||
|
| "Customization"
|
||||||
|
| "RailjackImage"
|
||||||
|
| "CrewMembers"
|
||||||
|
| "Details"
|
||||||
|
> {
|
||||||
|
// Warframe sends an ItemId instead _id, it will be converted to _id before being pushed to the inventory
|
||||||
|
ItemId: IOid;
|
||||||
|
Favorite?: boolean;
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
22
src/controllers/api/saveSettingsController.ts
Normal file
22
src/controllers/api/saveSettingsController.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { ISettings } from "../../types/inventoryTypes/inventoryTypes";
|
||||||
|
|
||||||
|
interface ISaveSettingsRequest {
|
||||||
|
Settings: ISettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveSettingsController: RequestHandler = async (req, res): Promise<void> => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
|
||||||
|
const settingResults = getJSONfromString<ISaveSettingsRequest>(String(req.body));
|
||||||
|
|
||||||
|
const inventory = await getInventory(accountId);
|
||||||
|
inventory.Settings = Object.assign(inventory.Settings, settingResults.Settings);
|
||||||
|
await inventory.save();
|
||||||
|
res.json(inventory.Settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { saveSettingsController };
|
112
src/controllers/custom/manageQuestsController.ts
Normal file
112
src/controllers/custom/manageQuestsController.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { addString } from "@/src/controllers/api/inventoryController";
|
||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
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";
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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": {
|
||||||
|
logger.info("resetting all quests..");
|
||||||
|
for (const questKey of inventory.QuestKeys) {
|
||||||
|
questKey.Completed = false;
|
||||||
|
questKey.Progress = [];
|
||||||
|
}
|
||||||
|
inventory.ActiveQuest = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "completeAllUnlocked": {
|
||||||
|
logger.info("completing all unlocked quests..");
|
||||||
|
for (const questKey of inventory.QuestKeys) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.status(200).end();
|
||||||
|
};
|
@ -1249,6 +1249,7 @@ export type InventoryDocumentProps = {
|
|||||||
KahlLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
|
KahlLoadOuts: Types.DocumentArray<IOperatorConfigDatabase>;
|
||||||
PendingRecipes: Types.DocumentArray<IPendingRecipeDatabase>;
|
PendingRecipes: Types.DocumentArray<IPendingRecipeDatabase>;
|
||||||
WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>;
|
WeaponSkins: Types.DocumentArray<IWeaponSkinDatabase>;
|
||||||
|
QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
|
||||||
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
|
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
@ -35,6 +35,7 @@ import { gildWeaponController } from "@/src/controllers/api/gildWeaponController
|
|||||||
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
||||||
import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
|
import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
|
||||||
import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKey";
|
import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKey";
|
||||||
|
import { giveStartingGearController } from "../controllers/api/giveStartingGear";
|
||||||
import { guildTechController } from "../controllers/api/guildTechController";
|
import { guildTechController } from "../controllers/api/guildTechController";
|
||||||
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
|
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
|
||||||
import { hubController } from "@/src/controllers/api/hubController";
|
import { hubController } from "@/src/controllers/api/hubController";
|
||||||
@ -86,6 +87,7 @@ import { updateQuestController } from "@/src/controllers/api/updateQuestControll
|
|||||||
import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
|
import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController";
|
||||||
import { updateThemeController } from "../controllers/api/updateThemeController";
|
import { updateThemeController } from "../controllers/api/updateThemeController";
|
||||||
import { upgradesController } from "@/src/controllers/api/upgradesController";
|
import { upgradesController } from "@/src/controllers/api/upgradesController";
|
||||||
|
import { saveSettingsController } from "../controllers/api/saveSettingsController";
|
||||||
|
|
||||||
const apiRouter = express.Router();
|
const apiRouter = express.Router();
|
||||||
|
|
||||||
@ -145,6 +147,7 @@ apiRouter.post("/gildWeapon.php", gildWeaponController);
|
|||||||
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
|
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
|
||||||
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
|
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
|
||||||
apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
|
apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
|
||||||
|
apiRouter.post("/giveStartingGear.php", giveStartingGearController);
|
||||||
apiRouter.post("/guildTech.php", guildTechController);
|
apiRouter.post("/guildTech.php", guildTechController);
|
||||||
apiRouter.post("/hostSession.php", hostSessionController);
|
apiRouter.post("/hostSession.php", hostSessionController);
|
||||||
apiRouter.post("/infestedFoundry.php", infestedFoundryController);
|
apiRouter.post("/infestedFoundry.php", infestedFoundryController);
|
||||||
@ -182,5 +185,6 @@ apiRouter.post("/updateQuest.php", updateQuestController);
|
|||||||
apiRouter.post("/updateSession.php", updateSessionPostController);
|
apiRouter.post("/updateSession.php", updateSessionPostController);
|
||||||
apiRouter.post("/updateTheme.php", updateThemeController);
|
apiRouter.post("/updateTheme.php", updateThemeController);
|
||||||
apiRouter.post("/upgrades.php", upgradesController);
|
apiRouter.post("/upgrades.php", upgradesController);
|
||||||
|
apiRouter.post("/saveSettings.php", saveSettingsController);
|
||||||
|
|
||||||
export { apiRouter };
|
export { apiRouter };
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -38,6 +38,7 @@ import {
|
|||||||
ExportCustoms,
|
ExportCustoms,
|
||||||
ExportFlavour,
|
ExportFlavour,
|
||||||
ExportGear,
|
ExportGear,
|
||||||
|
ExportKeys,
|
||||||
ExportRecipes,
|
ExportRecipes,
|
||||||
ExportResources,
|
ExportResources,
|
||||||
ExportSentinels,
|
ExportSentinels,
|
||||||
@ -365,6 +366,19 @@ export const addItem = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (typeName in ExportKeys) {
|
||||||
|
// Note: "/Lotus/Types/Keys/" contains some EmailItems
|
||||||
|
inventory.QuestKeys.push({ ItemType: typeName });
|
||||||
|
return {
|
||||||
|
InventoryChanges: {
|
||||||
|
QuestKeys: [
|
||||||
|
{
|
||||||
|
ItemType: typeName
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Path-based duck typing
|
// Path-based duck typing
|
||||||
switch (typeName.substr(1).split("/")[1]) {
|
switch (typeName.substr(1).split("/")[1]) {
|
||||||
@ -481,18 +495,6 @@ export const addItem = async (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Keys": {
|
|
||||||
inventory.QuestKeys.push({ ItemType: typeName });
|
|
||||||
return {
|
|
||||||
InventoryChanges: {
|
|
||||||
QuestKeys: [
|
|
||||||
{
|
|
||||||
ItemType: typeName
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case "NeutralCreatures": {
|
case "NeutralCreatures": {
|
||||||
const horseIndex = inventory.Horses.push({ ItemType: typeName });
|
const horseIndex = inventory.Horses.push({ ItemType: typeName });
|
||||||
return {
|
return {
|
||||||
@ -514,12 +516,15 @@ export const addItem = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case "Vehicles":
|
||||||
|
if (typeName == "/Lotus/Types/Vehicles/Motorcycle/MotorcyclePowerSuit") {
|
||||||
|
return { InventoryChanges: addMotorcycle(inventory, typeName) };
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const errorMessage = `unable to add item: ${typeName}`;
|
break;
|
||||||
logger.error(errorMessage);
|
}
|
||||||
throw new Error(errorMessage);
|
throw new Error(`unable to add item: ${typeName}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addItems = async (
|
export const addItems = async (
|
||||||
@ -828,9 +833,12 @@ const addCrewShip = (
|
|||||||
typeName: string,
|
typeName: string,
|
||||||
inventoryChanges: IInventoryChanges = {}
|
inventoryChanges: IInventoryChanges = {}
|
||||||
): IInventoryChanges => {
|
): IInventoryChanges => {
|
||||||
|
if (inventory.CrewShips.length != 0) {
|
||||||
|
throw new Error("refusing to add CrewShip because account already has one");
|
||||||
|
}
|
||||||
const index = inventory.CrewShips.push({ ItemType: typeName }) - 1;
|
const index = inventory.CrewShips.push({ ItemType: typeName }) - 1;
|
||||||
inventoryChanges.CrewShips ??= [];
|
inventoryChanges.CrewShips ??= [];
|
||||||
(inventoryChanges.CrewShips as object[]).push(inventory.CrewShips[index].toJSON());
|
(inventoryChanges.CrewShips as IEquipmentClient[]).push(inventory.CrewShips[index].toJSON<IEquipmentClient>());
|
||||||
return inventoryChanges;
|
return inventoryChanges;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -839,9 +847,28 @@ const addCrewShipHarness = (
|
|||||||
typeName: string,
|
typeName: string,
|
||||||
inventoryChanges: IInventoryChanges = {}
|
inventoryChanges: IInventoryChanges = {}
|
||||||
): IInventoryChanges => {
|
): IInventoryChanges => {
|
||||||
|
if (inventory.CrewShips.length != 0) {
|
||||||
|
throw new Error("refusing to add CrewShipHarness because account already has one");
|
||||||
|
}
|
||||||
const index = inventory.CrewShipHarnesses.push({ ItemType: typeName }) - 1;
|
const index = inventory.CrewShipHarnesses.push({ ItemType: typeName }) - 1;
|
||||||
inventoryChanges.CrewShipHarnesses ??= [];
|
inventoryChanges.CrewShipHarnesses ??= [];
|
||||||
(inventoryChanges.CrewShipHarnesses as object[]).push(inventory.CrewShipHarnesses[index].toJSON());
|
(inventoryChanges.CrewShipHarnesses as IEquipmentClient[]).push(
|
||||||
|
inventory.CrewShipHarnesses[index].toJSON<IEquipmentClient>()
|
||||||
|
);
|
||||||
|
return inventoryChanges;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addMotorcycle = (
|
||||||
|
inventory: TInventoryDatabaseDocument,
|
||||||
|
typeName: string,
|
||||||
|
inventoryChanges: IInventoryChanges = {}
|
||||||
|
): IInventoryChanges => {
|
||||||
|
if (inventory.Motorcycles.length != 0) {
|
||||||
|
throw new Error("refusing to add Motorcycle because account already has one");
|
||||||
|
}
|
||||||
|
const index = inventory.Motorcycles.push({ ItemType: typeName }) - 1;
|
||||||
|
inventoryChanges.Motorcycles ??= [];
|
||||||
|
(inventoryChanges.Motorcycles as IEquipmentClient[]).push(inventory.Motorcycles[index].toJSON<IEquipmentClient>());
|
||||||
return inventoryChanges;
|
return inventoryChanges;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1154,7 +1181,5 @@ export const addKeyChainItems = async (
|
|||||||
combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges);
|
combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
await addItems(inventory, nonStoreItems);
|
|
||||||
|
|
||||||
return inventoryChanges;
|
return inventoryChanges;
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
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 { 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 { 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 { ExportKeys } from "warframe-public-export-plus";
|
||||||
|
|
||||||
export interface IUpdateQuestRequest {
|
export interface IUpdateQuestRequest {
|
||||||
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[];
|
QuestKeys: Omit<IQuestKeyDatabase, "CompletionDate">[];
|
||||||
@ -62,3 +68,106 @@ 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 });
|
||||||
|
};
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -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`
|
||||||
};
|
};
|
||||||
|
@ -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: `Отправить`
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user