forked from OpenWF/SpaceNinjaServer
		
	Compare commits
	
		
			14 Commits
		
	
	
		
			4d23d1a131
			...
			acde743410
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| acde743410 | |||
| 1ecf53c96b | |||
| e67ef63b77 | |||
| 5772ebe746 | |||
| 0136e4d152 | |||
| 8b3ee4b4f5 | |||
| 6e8800f048 | |||
| d65a667acd | |||
| c6a3e86d2b | |||
| a8e41c95e7 | |||
| 9426359370 | |||
| e5247700df | |||
| 1c3f1e2276 | |||
| 7710e7c13f | 
@ -1,12 +1,13 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					import type { Request, RequestHandler } from "express";
 | 
				
			||||||
import { Inbox } from "../../models/inboxModel.ts";
 | 
					import { Inbox } from "../../models/inboxModel.ts";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    createMessage,
 | 
					    createMessage,
 | 
				
			||||||
    createNewEventMessages,
 | 
					 | 
				
			||||||
    deleteAllMessagesRead,
 | 
					    deleteAllMessagesRead,
 | 
				
			||||||
 | 
					    deleteAllMessagesReadNonCin,
 | 
				
			||||||
    deleteMessageRead,
 | 
					    deleteMessageRead,
 | 
				
			||||||
    getAllMessagesSorted,
 | 
					    getAllMessagesSorted,
 | 
				
			||||||
    getMessage
 | 
					    getMessage,
 | 
				
			||||||
 | 
					    type IMessageCreationTemplate
 | 
				
			||||||
} from "../../services/inboxService.ts";
 | 
					} from "../../services/inboxService.ts";
 | 
				
			||||||
import { getAccountForRequest, getAccountFromSuffixedName, getSuffixedName } from "../../services/loginService.ts";
 | 
					import { getAccountForRequest, getAccountFromSuffixedName, getSuffixedName } from "../../services/loginService.ts";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -21,6 +22,9 @@ import { ExportFlavour } from "warframe-public-export-plus";
 | 
				
			|||||||
import { handleStoreItemAcquisition } from "../../services/purchaseService.ts";
 | 
					import { handleStoreItemAcquisition } from "../../services/purchaseService.ts";
 | 
				
			||||||
import { fromStoreItem, isStoreItem } from "../../services/itemDataService.ts";
 | 
					import { fromStoreItem, isStoreItem } from "../../services/itemDataService.ts";
 | 
				
			||||||
import type { IOid } from "../../types/commonTypes.ts";
 | 
					import type { IOid } from "../../types/commonTypes.ts";
 | 
				
			||||||
 | 
					import { unixTimesInMs } from "../../constants/timeConstants.ts";
 | 
				
			||||||
 | 
					import { config } from "../../services/configService.ts";
 | 
				
			||||||
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const inboxController: RequestHandler = async (req, res) => {
 | 
					export const inboxController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
 | 
					    const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
 | 
				
			||||||
@ -31,11 +35,11 @@ export const inboxController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    if (deleteId) {
 | 
					    if (deleteId) {
 | 
				
			||||||
        if (deleteId === "DeleteAllRead") {
 | 
					        if (deleteId === "DeleteAllRead") {
 | 
				
			||||||
            await deleteAllMessagesRead(accountId);
 | 
					            await deleteAllMessagesRead(accountId);
 | 
				
			||||||
            res.status(200).end();
 | 
					        } else if (deleteId === "DeleteAllReadNonCin") {
 | 
				
			||||||
            return;
 | 
					            await deleteAllMessagesReadNonCin(accountId);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            await deleteMessageRead(parseOid(deleteId as string));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        await deleteMessageRead(parseOid(deleteId as string));
 | 
					 | 
				
			||||||
        res.status(200).end();
 | 
					        res.status(200).end();
 | 
				
			||||||
    } else if (messageId) {
 | 
					    } else if (messageId) {
 | 
				
			||||||
        const message = await getMessage(parseOid(messageId as string));
 | 
					        const message = await getMessage(parseOid(messageId as string));
 | 
				
			||||||
@ -134,6 +138,119 @@ export const inboxController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const createNewEventMessages = async (req: Request): Promise<void> => {
 | 
				
			||||||
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
 | 
					    const newEventMessages: IMessageCreationTemplate[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Baro
 | 
				
			||||||
 | 
					    const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
 | 
				
			||||||
 | 
					    const baroStart = baroIndex * (unixTimesInMs.day * 14) + 910800000;
 | 
				
			||||||
 | 
					    const baroActualStart = baroStart + unixTimesInMs.day * (config.worldState?.baroAlwaysAvailable ? 0 : 12);
 | 
				
			||||||
 | 
					    if (Date.now() >= baroActualStart && account.LatestEventMessageDate.getTime() < baroActualStart) {
 | 
				
			||||||
 | 
					        newEventMessages.push({
 | 
				
			||||||
 | 
					            sndr: "/Lotus/Language/G1Quests/VoidTraderName",
 | 
				
			||||||
 | 
					            sub: "/Lotus/Language/CommunityMessages/VoidTraderAppearanceTitle",
 | 
				
			||||||
 | 
					            msg: "/Lotus/Language/CommunityMessages/VoidTraderAppearanceMessage",
 | 
				
			||||||
 | 
					            icon: "/Lotus/Interface/Icons/Npcs/BaroKiTeerPortrait.png",
 | 
				
			||||||
 | 
					            startDate: new Date(baroActualStart),
 | 
				
			||||||
 | 
					            endDate: new Date(baroStart + unixTimesInMs.day * 14),
 | 
				
			||||||
 | 
					            CrossPlatform: true,
 | 
				
			||||||
 | 
					            arg: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Key: "NODE_NAME",
 | 
				
			||||||
 | 
					                    Tag: ["EarthHUB", "MercuryHUB", "SaturnHUB", "PlutoHUB"][baroIndex % 4]
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            date: new Date(baroActualStart)
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // BUG: Deleting the inbox message manually means it'll just be automatically re-created. This is because we don't use startDate/endDate for these config-toggled events.
 | 
				
			||||||
 | 
					    const promises = [];
 | 
				
			||||||
 | 
					    if (config.worldState?.creditBoost) {
 | 
				
			||||||
 | 
					        promises.push(
 | 
				
			||||||
 | 
					            (async (): Promise<void> => {
 | 
				
			||||||
 | 
					                if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666672" }))) {
 | 
				
			||||||
 | 
					                    newEventMessages.push({
 | 
				
			||||||
 | 
					                        globaUpgradeId: new Types.ObjectId("5b23106f283a555109666672"),
 | 
				
			||||||
 | 
					                        sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
 | 
				
			||||||
 | 
					                        sub: "/Lotus/Language/Items/EventDoubleCreditsName",
 | 
				
			||||||
 | 
					                        msg: "/Lotus/Language/Items/EventDoubleCreditsDesc",
 | 
				
			||||||
 | 
					                        icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
 | 
				
			||||||
 | 
					                        startDate: new Date(),
 | 
				
			||||||
 | 
					                        CrossPlatform: true
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (config.worldState?.affinityBoost) {
 | 
				
			||||||
 | 
					        promises.push(
 | 
				
			||||||
 | 
					            (async (): Promise<void> => {
 | 
				
			||||||
 | 
					                if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666673" }))) {
 | 
				
			||||||
 | 
					                    newEventMessages.push({
 | 
				
			||||||
 | 
					                        globaUpgradeId: new Types.ObjectId("5b23106f283a555109666673"),
 | 
				
			||||||
 | 
					                        sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
 | 
				
			||||||
 | 
					                        sub: "/Lotus/Language/Items/EventDoubleAffinityName",
 | 
				
			||||||
 | 
					                        msg: "/Lotus/Language/Items/EventDoubleAffinityDesc",
 | 
				
			||||||
 | 
					                        icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
 | 
				
			||||||
 | 
					                        startDate: new Date(),
 | 
				
			||||||
 | 
					                        CrossPlatform: true
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (config.worldState?.resourceBoost) {
 | 
				
			||||||
 | 
					        promises.push(
 | 
				
			||||||
 | 
					            (async (): Promise<void> => {
 | 
				
			||||||
 | 
					                if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666674" }))) {
 | 
				
			||||||
 | 
					                    newEventMessages.push({
 | 
				
			||||||
 | 
					                        globaUpgradeId: new Types.ObjectId("5b23106f283a555109666674"),
 | 
				
			||||||
 | 
					                        sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
 | 
				
			||||||
 | 
					                        sub: "/Lotus/Language/Items/EventDoubleResourceName",
 | 
				
			||||||
 | 
					                        msg: "/Lotus/Language/Items/EventDoubleResourceDesc",
 | 
				
			||||||
 | 
					                        icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
 | 
				
			||||||
 | 
					                        startDate: new Date(),
 | 
				
			||||||
 | 
					                        CrossPlatform: true
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (config.worldState?.galleonOfGhouls) {
 | 
				
			||||||
 | 
					        promises.push(
 | 
				
			||||||
 | 
					            (async (): Promise<void> => {
 | 
				
			||||||
 | 
					                if (!(await Inbox.exists({ ownerId: account._id, goalTag: "GalleonRobbery" }))) {
 | 
				
			||||||
 | 
					                    newEventMessages.push({
 | 
				
			||||||
 | 
					                        sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
 | 
				
			||||||
 | 
					                        sub: "/Lotus/Language/Events/GalleonRobberyIntroMsgTitle",
 | 
				
			||||||
 | 
					                        msg: "/Lotus/Language/Events/GalleonRobberyIntroMsgDesc",
 | 
				
			||||||
 | 
					                        icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png",
 | 
				
			||||||
 | 
					                        transmission: "/Lotus/Sounds/Dialog/GalleonOfGhouls/DGhoulsWeekOneInbox0010VayHek",
 | 
				
			||||||
 | 
					                        att: ["/Lotus/Upgrades/Skins/Events/OgrisOldSchool"],
 | 
				
			||||||
 | 
					                        startDate: new Date(),
 | 
				
			||||||
 | 
					                        goalTag: "GalleonRobbery"
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (newEventMessages.length === 0) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await createMessage(account._id, newEventMessages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const latestEventMessage = newEventMessages.reduce((prev, current) =>
 | 
				
			||||||
 | 
					        prev.startDate! > current.startDate! ? prev : current
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    account.LatestEventMessageDate = new Date(latestEventMessage.startDate!);
 | 
				
			||||||
 | 
					    await account.save();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 33.6.0 has query arguments like lastMessage={"$oid":"68112baebf192e786d1502bb"} instead of lastMessage=68112baebf192e786d1502bb
 | 
					// 33.6.0 has query arguments like lastMessage={"$oid":"68112baebf192e786d1502bb"} instead of lastMessage=68112baebf192e786d1502bb
 | 
				
			||||||
const parseOid = (oid: string): string => {
 | 
					const parseOid = (oid: string): string => {
 | 
				
			||||||
    if (oid[0] == "{") {
 | 
					    if (oid[0] == "{") {
 | 
				
			||||||
 | 
				
			|||||||
@ -335,15 +335,16 @@ export const getInventoryResponse = async (
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (config.unlockAllSkins) {
 | 
					    if (config.unlockAllSkins) {
 | 
				
			||||||
        const missingWeaponSkins = new Set(Object.keys(ExportCustoms));
 | 
					        const ownedWeaponSkins = new Set<string>(inventoryResponse.WeaponSkins.map(x => x.ItemType));
 | 
				
			||||||
        inventoryResponse.WeaponSkins.forEach(x => missingWeaponSkins.delete(x.ItemType));
 | 
					        for (const [uniqueName, meta] of Object.entries(ExportCustoms)) {
 | 
				
			||||||
        for (const uniqueName of missingWeaponSkins) {
 | 
					            if (!meta.alwaysAvailable && !ownedWeaponSkins.has(uniqueName)) {
 | 
				
			||||||
            inventoryResponse.WeaponSkins.push({
 | 
					                inventoryResponse.WeaponSkins.push({
 | 
				
			||||||
                ItemId: {
 | 
					                    ItemId: {
 | 
				
			||||||
                    $oid: "ca70ca70ca70ca70" + catBreadHash(uniqueName).toString(16).padStart(8, "0")
 | 
					                        $oid: "ca70ca70ca70ca70" + catBreadHash(uniqueName).toString(16).padStart(8, "0")
 | 
				
			||||||
                },
 | 
					                    },
 | 
				
			||||||
                ItemType: uniqueName
 | 
					                    ItemType: uniqueName
 | 
				
			||||||
            });
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,9 @@ import type { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "../../
 | 
				
			|||||||
import { logger } from "../../utils/logger.ts";
 | 
					import { logger } from "../../utils/logger.ts";
 | 
				
			||||||
import { version_compare } from "../../helpers/inventoryHelpers.ts";
 | 
					import { version_compare } from "../../helpers/inventoryHelpers.ts";
 | 
				
			||||||
import { handleNonceInvalidation } from "../../services/wsService.ts";
 | 
					import { handleNonceInvalidation } from "../../services/wsService.ts";
 | 
				
			||||||
 | 
					import { getInventory } from "../../services/inventoryService.ts";
 | 
				
			||||||
 | 
					import { createMessage } from "../../services/inboxService.ts";
 | 
				
			||||||
 | 
					import { fromStoreItem } from "../../services/itemDataService.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const loginController: RequestHandler = async (request, response) => {
 | 
					export const loginController: RequestHandler = async (request, response) => {
 | 
				
			||||||
    const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object
 | 
					    const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object
 | 
				
			||||||
@ -76,6 +79,24 @@ export const loginController: RequestHandler = async (request, response) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    handleNonceInvalidation(account._id.toString());
 | 
					    handleNonceInvalidation(account._id.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // If the client crashed during an endless fissure mission, discharge rewards to an inbox message. (https://www.reddit.com/r/Warframe/comments/5uwwjm/til_if_you_crash_during_a_fissure_you_keep_any/)
 | 
				
			||||||
 | 
					    const inventory = await getInventory(account._id.toString(), "MissionRelicRewards");
 | 
				
			||||||
 | 
					    if (inventory.MissionRelicRewards) {
 | 
				
			||||||
 | 
					        await createMessage(account._id, [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                sndr: "/Lotus/Language/Bosses/Ordis",
 | 
				
			||||||
 | 
					                msg: "/Lotus/Language/Menu/VoidProjectionItemsMessage",
 | 
				
			||||||
 | 
					                sub: "/Lotus/Language/Menu/VoidProjectionItemsSubject",
 | 
				
			||||||
 | 
					                icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
 | 
				
			||||||
 | 
					                countedAtt: inventory.MissionRelicRewards.map(x => ({ ...x, ItemType: fromStoreItem(x.ItemType) })),
 | 
				
			||||||
 | 
					                attVisualOnly: true,
 | 
				
			||||||
 | 
					                highPriority: true // TOVERIFY
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        inventory.MissionRelicRewards = undefined;
 | 
				
			||||||
 | 
					        await inventory.save();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel));
 | 
					    response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -115,7 +115,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
                if (stage > 0) {
 | 
					                if (stage > 0) {
 | 
				
			||||||
                    await giveKeyChainStageTriggered(inventory, {
 | 
					                    await giveKeyChainStageTriggered(inventory, {
 | 
				
			||||||
                        KeyChain: questKey.ItemType,
 | 
					                        KeyChain: questKey.ItemType,
 | 
				
			||||||
                        ChainStage: stage
 | 
					                        ChainStage: stage - 1
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ import type {
 | 
				
			|||||||
} from "../types/inventoryTypes/commonInventoryTypes.ts";
 | 
					} from "../types/inventoryTypes/commonInventoryTypes.ts";
 | 
				
			||||||
import type { IMongoDate } from "../types/commonTypes.ts";
 | 
					import type { IMongoDate } from "../types/commonTypes.ts";
 | 
				
			||||||
import type {
 | 
					import type {
 | 
				
			||||||
 | 
					    IBooster,
 | 
				
			||||||
    IDialogueClient,
 | 
					    IDialogueClient,
 | 
				
			||||||
    IDialogueDatabase,
 | 
					    IDialogueDatabase,
 | 
				
			||||||
    IDialogueHistoryClient,
 | 
					    IDialogueHistoryClient,
 | 
				
			||||||
@ -463,6 +464,9 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
 | 
				
			|||||||
    if (client.Accolades !== undefined) {
 | 
					    if (client.Accolades !== undefined) {
 | 
				
			||||||
        db.Accolades = client.Accolades;
 | 
					        db.Accolades = client.Accolades;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (client.Boosters !== undefined) {
 | 
				
			||||||
 | 
					        replaceArray<IBooster>(db.Boosters, client.Boosters);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const importLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {
 | 
					export const importLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,6 @@
 | 
				
			|||||||
import type { IMessageDatabase } from "../models/inboxModel.ts";
 | 
					import type { IMessageDatabase } from "../models/inboxModel.ts";
 | 
				
			||||||
import { Inbox } from "../models/inboxModel.ts";
 | 
					import { Inbox } from "../models/inboxModel.ts";
 | 
				
			||||||
import { getAccountForRequest } from "./loginService.ts";
 | 
					import type { HydratedDocument, Types } from "mongoose";
 | 
				
			||||||
import type { HydratedDocument } from "mongoose";
 | 
					 | 
				
			||||||
import { Types } from "mongoose";
 | 
					 | 
				
			||||||
import type { Request } from "express";
 | 
					 | 
				
			||||||
import { unixTimesInMs } from "../constants/timeConstants.ts";
 | 
					 | 
				
			||||||
import { config } from "./configService.ts";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getAllMessagesSorted = async (accountId: string): Promise<HydratedDocument<IMessageDatabase>[]> => {
 | 
					export const getAllMessagesSorted = async (accountId: string): Promise<HydratedDocument<IMessageDatabase>[]> => {
 | 
				
			||||||
    const inbox = await Inbox.find({ ownerId: accountId }).sort({ date: -1 });
 | 
					    const inbox = await Inbox.find({ ownerId: accountId }).sort({ date: -1 });
 | 
				
			||||||
@ -29,117 +24,8 @@ export const deleteAllMessagesRead = async (accountId: string): Promise<void> =>
 | 
				
			|||||||
    await Inbox.deleteMany({ ownerId: accountId, r: true });
 | 
					    await Inbox.deleteMany({ ownerId: accountId, r: true });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const createNewEventMessages = async (req: Request): Promise<void> => {
 | 
					export const deleteAllMessagesReadNonCin = async (accountId: string): Promise<void> => {
 | 
				
			||||||
    const account = await getAccountForRequest(req);
 | 
					    await Inbox.deleteMany({ ownerId: accountId, r: true, cinematic: null });
 | 
				
			||||||
    const newEventMessages: IMessageCreationTemplate[] = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Baro
 | 
					 | 
				
			||||||
    const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
 | 
					 | 
				
			||||||
    const baroStart = baroIndex * (unixTimesInMs.day * 14) + 910800000;
 | 
					 | 
				
			||||||
    const baroActualStart = baroStart + unixTimesInMs.day * (config.worldState?.baroAlwaysAvailable ? 0 : 12);
 | 
					 | 
				
			||||||
    if (Date.now() >= baroActualStart && account.LatestEventMessageDate.getTime() < baroActualStart) {
 | 
					 | 
				
			||||||
        newEventMessages.push({
 | 
					 | 
				
			||||||
            sndr: "/Lotus/Language/G1Quests/VoidTraderName",
 | 
					 | 
				
			||||||
            sub: "/Lotus/Language/CommunityMessages/VoidTraderAppearanceTitle",
 | 
					 | 
				
			||||||
            msg: "/Lotus/Language/CommunityMessages/VoidTraderAppearanceMessage",
 | 
					 | 
				
			||||||
            icon: "/Lotus/Interface/Icons/Npcs/BaroKiTeerPortrait.png",
 | 
					 | 
				
			||||||
            startDate: new Date(baroActualStart),
 | 
					 | 
				
			||||||
            endDate: new Date(baroStart + unixTimesInMs.day * 14),
 | 
					 | 
				
			||||||
            CrossPlatform: true,
 | 
					 | 
				
			||||||
            arg: [
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Key: "NODE_NAME",
 | 
					 | 
				
			||||||
                    Tag: ["EarthHUB", "MercuryHUB", "SaturnHUB", "PlutoHUB"][baroIndex % 4]
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            date: new Date(baroActualStart)
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // BUG: Deleting the inbox message manually means it'll just be automatically re-created. This is because we don't use startDate/endDate for these config-toggled events.
 | 
					 | 
				
			||||||
    const promises = [];
 | 
					 | 
				
			||||||
    if (config.worldState?.creditBoost) {
 | 
					 | 
				
			||||||
        promises.push(
 | 
					 | 
				
			||||||
            (async (): Promise<void> => {
 | 
					 | 
				
			||||||
                if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666672" }))) {
 | 
					 | 
				
			||||||
                    newEventMessages.push({
 | 
					 | 
				
			||||||
                        globaUpgradeId: new Types.ObjectId("5b23106f283a555109666672"),
 | 
					 | 
				
			||||||
                        sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
 | 
					 | 
				
			||||||
                        sub: "/Lotus/Language/Items/EventDoubleCreditsName",
 | 
					 | 
				
			||||||
                        msg: "/Lotus/Language/Items/EventDoubleCreditsDesc",
 | 
					 | 
				
			||||||
                        icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
 | 
					 | 
				
			||||||
                        startDate: new Date(),
 | 
					 | 
				
			||||||
                        CrossPlatform: true
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            })()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (config.worldState?.affinityBoost) {
 | 
					 | 
				
			||||||
        promises.push(
 | 
					 | 
				
			||||||
            (async (): Promise<void> => {
 | 
					 | 
				
			||||||
                if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666673" }))) {
 | 
					 | 
				
			||||||
                    newEventMessages.push({
 | 
					 | 
				
			||||||
                        globaUpgradeId: new Types.ObjectId("5b23106f283a555109666673"),
 | 
					 | 
				
			||||||
                        sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
 | 
					 | 
				
			||||||
                        sub: "/Lotus/Language/Items/EventDoubleAffinityName",
 | 
					 | 
				
			||||||
                        msg: "/Lotus/Language/Items/EventDoubleAffinityDesc",
 | 
					 | 
				
			||||||
                        icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
 | 
					 | 
				
			||||||
                        startDate: new Date(),
 | 
					 | 
				
			||||||
                        CrossPlatform: true
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            })()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (config.worldState?.resourceBoost) {
 | 
					 | 
				
			||||||
        promises.push(
 | 
					 | 
				
			||||||
            (async (): Promise<void> => {
 | 
					 | 
				
			||||||
                if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666674" }))) {
 | 
					 | 
				
			||||||
                    newEventMessages.push({
 | 
					 | 
				
			||||||
                        globaUpgradeId: new Types.ObjectId("5b23106f283a555109666674"),
 | 
					 | 
				
			||||||
                        sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
 | 
					 | 
				
			||||||
                        sub: "/Lotus/Language/Items/EventDoubleResourceName",
 | 
					 | 
				
			||||||
                        msg: "/Lotus/Language/Items/EventDoubleResourceDesc",
 | 
					 | 
				
			||||||
                        icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
 | 
					 | 
				
			||||||
                        startDate: new Date(),
 | 
					 | 
				
			||||||
                        CrossPlatform: true
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            })()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (config.worldState?.galleonOfGhouls) {
 | 
					 | 
				
			||||||
        promises.push(
 | 
					 | 
				
			||||||
            (async (): Promise<void> => {
 | 
					 | 
				
			||||||
                if (!(await Inbox.exists({ ownerId: account._id, goalTag: "GalleonRobbery" }))) {
 | 
					 | 
				
			||||||
                    newEventMessages.push({
 | 
					 | 
				
			||||||
                        sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
 | 
					 | 
				
			||||||
                        sub: "/Lotus/Language/Events/GalleonRobberyIntroMsgTitle",
 | 
					 | 
				
			||||||
                        msg: "/Lotus/Language/Events/GalleonRobberyIntroMsgDesc",
 | 
					 | 
				
			||||||
                        icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png",
 | 
					 | 
				
			||||||
                        transmission: "/Lotus/Sounds/Dialog/GalleonOfGhouls/DGhoulsWeekOneInbox0010VayHek",
 | 
					 | 
				
			||||||
                        att: ["/Lotus/Upgrades/Skins/Events/OgrisOldSchool"],
 | 
					 | 
				
			||||||
                        startDate: new Date(),
 | 
					 | 
				
			||||||
                        goalTag: "GalleonRobbery"
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            })()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    await Promise.all(promises);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (newEventMessages.length === 0) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    await createMessage(account._id, newEventMessages);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const latestEventMessage = newEventMessages.reduce((prev, current) =>
 | 
					 | 
				
			||||||
        prev.startDate! > current.startDate! ? prev : current
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    account.LatestEventMessageDate = new Date(latestEventMessage.startDate!);
 | 
					 | 
				
			||||||
    await account.save();
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const createMessage = async (
 | 
					export const createMessage = async (
 | 
				
			||||||
 | 
				
			|||||||
@ -331,7 +331,7 @@ export const giveKeyChainMessage = async (
 | 
				
			|||||||
): Promise<void> => {
 | 
					): Promise<void> => {
 | 
				
			||||||
    const keyChainMessage = getKeyChainMessage(keyChainInfo);
 | 
					    const keyChainMessage = getKeyChainMessage(keyChainInfo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (questKey.Progress![0].c > 0) {
 | 
					    if ((questKey.Progress?.[0]?.c ?? 0) > 0) {
 | 
				
			||||||
        keyChainMessage.att = [];
 | 
					        keyChainMessage.att = [];
 | 
				
			||||||
        keyChainMessage.countedAtt = [];
 | 
					        keyChainMessage.countedAtt = [];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -280,6 +280,14 @@ export const getSortie = (day: number): ISortie => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const willHaveAssassination = boss != "SORTIE_BOSS_CORRUPTED_VOR" && rng.randomInt(0, 2) == 2;
 | 
				
			||||||
 | 
					    if (willHaveAssassination) {
 | 
				
			||||||
 | 
					        const index = nodes.indexOf(sortieBossNode[boss]);
 | 
				
			||||||
 | 
					        if (index != -1) {
 | 
				
			||||||
 | 
					            nodes.splice(index, 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const selectedNodes: ISortieMission[] = [];
 | 
					    const selectedNodes: ISortieMission[] = [];
 | 
				
			||||||
    const missionTypes = new Set();
 | 
					    const missionTypes = new Set();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -309,7 +317,7 @@ export const getSortie = (day: number): ISortie => {
 | 
				
			|||||||
            "SORTIE_MODIFIER_BOW_ONLY"
 | 
					            "SORTIE_MODIFIER_BOW_ONLY"
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (i == 2 && boss != "SORTIE_BOSS_CORRUPTED_VOR" && rng.randomInt(0, 2) == 2) {
 | 
					        if (i == 2 && willHaveAssassination) {
 | 
				
			||||||
            const tileset = sortieTilesets[sortieBossNode[boss] as keyof typeof sortieTilesets] as TSortieTileset;
 | 
					            const tileset = sortieTilesets[sortieBossNode[boss] as keyof typeof sortieTilesets] as TSortieTileset;
 | 
				
			||||||
            pushTilesetModifiers(modifiers, tileset);
 | 
					            pushTilesetModifiers(modifiers, tileset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -361,7 +369,9 @@ export const getSortie = (day: number): ISortie => {
 | 
				
			|||||||
        Activation: { $date: { $numberLong: dayStart.toString() } },
 | 
					        Activation: { $date: { $numberLong: dayStart.toString() } },
 | 
				
			||||||
        Expiry: { $date: { $numberLong: dayEnd.toString() } },
 | 
					        Expiry: { $date: { $numberLong: dayEnd.toString() } },
 | 
				
			||||||
        Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards",
 | 
					        Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards",
 | 
				
			||||||
        Seed: seed,
 | 
					        Seed: selectedNodes.find(x => x.tileset == "CorpusIcePlanetTileset")
 | 
				
			||||||
 | 
					            ? 2081 // this seed produces 12 zeroes in a row if asked to pick (0, 1); this way the CorpusIcePlanetTileset image is always index 0, the 'correct' choice.
 | 
				
			||||||
 | 
					            : seed,
 | 
				
			||||||
        Boss: boss,
 | 
					        Boss: boss,
 | 
				
			||||||
        Variants: selectedNodes
 | 
					        Variants: selectedNodes
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
				
			|||||||
@ -193,7 +193,7 @@ dict = {
 | 
				
			|||||||
    cheats_skipTutorial: `Tutorial überspringen`,
 | 
					    cheats_skipTutorial: `Tutorial überspringen`,
 | 
				
			||||||
    cheats_skipAllDialogue: `Alle Dialoge überspringen`,
 | 
					    cheats_skipAllDialogue: `Alle Dialoge überspringen`,
 | 
				
			||||||
    cheats_unlockAllScans: `Alle Scans freischalten`,
 | 
					    cheats_unlockAllScans: `Alle Scans freischalten`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please note that you'll need to relog for the client to refresh this.`,
 | 
				
			||||||
    cheats_unlockAllMissions: `Alle Missionen freischalten`,
 | 
					    cheats_unlockAllMissions: `Alle Missionen freischalten`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `Erfolgreich. Bitte beachte, dass du ein Dojo/Relais besuchen oder dich neu einloggen musst, damit die Sternenkarte aktualisiert wird.`,
 | 
					    cheats_unlockAllMissions_ok: `Erfolgreich. Bitte beachte, dass du ein Dojo/Relais besuchen oder dich neu einloggen musst, damit die Sternenkarte aktualisiert wird.`,
 | 
				
			||||||
    cheats_infiniteCredits: `Unendlich Credits`,
 | 
					    cheats_infiniteCredits: `Unendlich Credits`,
 | 
				
			||||||
@ -227,7 +227,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `Baro hat volles Inventar`,
 | 
					    cheats_baroFullyStocked: `Baro hat volles Inventar`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
 | 
					    cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Alle Profiteintreiber-Phasen freischalten`,
 | 
					    cheats_unlockAllProfitTakerStages: `Alle Profiteintreiber-Phasen freischalten`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command in game chat, visiting a dojo/relay, or relogging.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Riven-Mod Herausforderung sofort abschließen`,
 | 
					    cheats_instantFinishRivenChallenge: `Riven-Mod Herausforderung sofort abschließen`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`,
 | 
					    cheats_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`,
 | 
					    cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
 | 
					    general_inventoryUpdateNote: `Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command in game chat, visiting a dojo/relay, or relogging.`,
 | 
				
			||||||
    general_inventoryUpdateNoteGameWs: `Note: You may need to reopen any menu you are on for changes to be reflected.`,
 | 
					    general_inventoryUpdateNoteGameWs: `Note: You may need to reopen any menu you are on for changes to be reflected.`,
 | 
				
			||||||
    general_addButton: `Add`,
 | 
					    general_addButton: `Add`,
 | 
				
			||||||
    general_setButton: `Set`,
 | 
					    general_setButton: `Set`,
 | 
				
			||||||
@ -192,7 +192,7 @@ dict = {
 | 
				
			|||||||
    cheats_skipTutorial: `Skip Tutorial`,
 | 
					    cheats_skipTutorial: `Skip Tutorial`,
 | 
				
			||||||
    cheats_skipAllDialogue: `Skip All Dialogue`,
 | 
					    cheats_skipAllDialogue: `Skip All Dialogue`,
 | 
				
			||||||
    cheats_unlockAllScans: `Unlock All Scans`,
 | 
					    cheats_unlockAllScans: `Unlock All Scans`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					    cheats_unlockSuccRelog: `Success. Please note that you'll need to relog for the client to refresh this.`,
 | 
				
			||||||
    cheats_unlockAllMissions: `Unlock All Missions`,
 | 
					    cheats_unlockAllMissions: `Unlock All Missions`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `Success. Please note that you'll need to enter a dojo/relay or relog for the client to refresh the star chart.`,
 | 
					    cheats_unlockAllMissions_ok: `Success. Please note that you'll need to enter a dojo/relay or relog for the client to refresh the star chart.`,
 | 
				
			||||||
    cheats_infiniteCredits: `Infinite Credits`,
 | 
					    cheats_infiniteCredits: `Infinite Credits`,
 | 
				
			||||||
@ -226,7 +226,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `Baro Fully Stocked`,
 | 
					    cheats_baroFullyStocked: `Baro Fully Stocked`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Syndicate Missions Repeatable`,
 | 
					    cheats_syndicateMissionsRepeatable: `Syndicate Missions Repeatable`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Unlock All Profit Taker Stages`,
 | 
					    cheats_unlockAllProfitTakerStages: `Unlock All Profit Taker Stages`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSuccInventory: `Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command in game chat, visiting a dojo/relay, or relogging.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Instant Finish Riven Challenge`,
 | 
					    cheats_instantFinishRivenChallenge: `Instant Finish Riven Challenge`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
 | 
					    cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `No Resource Extractor Drones Damage`,
 | 
					    cheats_noResourceExtractorDronesDamage: `No Resource Extractor Drones Damage`,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
// Spanish translation by hxedcl
 | 
					// Spanish translation by hxedcl, Slayer55555
 | 
				
			||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `Para ver los cambios en el juego, necesitas volver a sincronizar tu inventario, por ejemplo, usando el comando /sync del bootstrapper, visitando un dojo o repetidor, o volviendo a iniciar sesión.`,
 | 
					    general_inventoryUpdateNote: `Para ver los cambios en el juego, necesitas volver a sincronizar tu inventario, por ejemplo, usando el comando /sync del bootstrapper, visitando un dojo o repetidor, o volviendo a iniciar sesión.`,
 | 
				
			||||||
    general_inventoryUpdateNoteGameWs: `Nota: Puede que necesites reabrir cualquier menú en el que te encuentres para que los cambios se reflejen.`,
 | 
					    general_inventoryUpdateNoteGameWs: `Nota: Puede que necesites reabrir cualquier menú en el que te encuentres para que los cambios se reflejen.`,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
// Chinese translation by meb154, bishan178, nyaoouo, qianlishun, CrazyZhang, Corvus, & qingchun
 | 
					// Chinese translation by meb154, bishan178, nyaoouo, qianlishun, CrazyZhang, Corvus, qingchun
 | 
				
			||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `注意: 要在游戏中查看更改,您需要重新同步库存,例如使用客户端的 /sync 命令,访问道场/中继站或重新登录.`,
 | 
					    general_inventoryUpdateNote: `注意: 要在游戏中查看更改,您需要重新同步库存,例如使用客户端的 /sync 命令,访问道场/中继站或重新登录.`,
 | 
				
			||||||
    general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`,
 | 
					    general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`,
 | 
				
			||||||
@ -193,7 +193,7 @@ dict = {
 | 
				
			|||||||
    cheats_skipTutorial: `跳过教程`,
 | 
					    cheats_skipTutorial: `跳过教程`,
 | 
				
			||||||
    cheats_skipAllDialogue: `跳过所有对话`,
 | 
					    cheats_skipAllDialogue: `跳过所有对话`,
 | 
				
			||||||
    cheats_unlockAllScans: `解锁所有扫描`,
 | 
					    cheats_unlockAllScans: `解锁所有扫描`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please note that you'll need to relog for the client to refresh this.`,
 | 
				
			||||||
    cheats_unlockAllMissions: `解锁所有星图`,
 | 
					    cheats_unlockAllMissions: `解锁所有星图`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `操作成功.请注意,您需要进入道场/中继站或重新登录以刷新星图数据.`,
 | 
					    cheats_unlockAllMissions_ok: `操作成功.请注意,您需要进入道场/中继站或重新登录以刷新星图数据.`,
 | 
				
			||||||
    cheats_infiniteCredits: `无限现金`,
 | 
					    cheats_infiniteCredits: `无限现金`,
 | 
				
			||||||
@ -227,7 +227,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `虚空商人贩卖所有商品`,
 | 
					    cheats_baroFullyStocked: `虚空商人贩卖所有商品`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `集团任务可重复完成`,
 | 
					    cheats_syndicateMissionsRepeatable: `集团任务可重复完成`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `解锁利润收割者圆蛛所有阶段`,
 | 
					    cheats_unlockAllProfitTakerStages: `解锁利润收割者圆蛛所有阶段`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command in game chat, visiting a dojo/relay, or relogging.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`,
 | 
					    cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `资源无人机即时完成`,
 | 
					    cheats_instantResourceExtractorDrones: `资源无人机即时完成`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `资源无人机不会损毁`,
 | 
					    cheats_noResourceExtractorDronesDamage: `资源无人机不会损毁`,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user