Compare commits
13 Commits
17e1eb86dd
...
e67ef63b77
| Author | SHA1 | Date | |
|---|---|---|---|
| e67ef63b77 | |||
| 5772ebe746 | |||
| 0136e4d152 | |||
| 8b3ee4b4f5 | |||
| 6e8800f048 | |||
| d65a667acd | |||
| c6a3e86d2b | |||
| a8e41c95e7 | |||
| 9426359370 | |||
| e5247700df | |||
| 1c3f1e2276 | |||
| 7710e7c13f | |||
| a64c5ea3c1 |
@ -38,6 +38,7 @@
|
||||
"anniversary": null,
|
||||
"hallowedNightmares": false,
|
||||
"hallowedNightmaresRewardsOverride": 0,
|
||||
"naberusNightsOverride": null,
|
||||
"proxyRebellion": false,
|
||||
"proxyRebellionRewardsOverride": 0,
|
||||
"galleonOfGhouls": 0,
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import type { Request, RequestHandler } from "express";
|
||||
import { Inbox } from "../../models/inboxModel.ts";
|
||||
import {
|
||||
createMessage,
|
||||
createNewEventMessages,
|
||||
deleteAllMessagesRead,
|
||||
deleteAllMessagesReadNonCin,
|
||||
deleteMessageRead,
|
||||
getAllMessagesSorted,
|
||||
getMessage
|
||||
getMessage,
|
||||
type IMessageCreationTemplate
|
||||
} from "../../services/inboxService.ts";
|
||||
import { getAccountForRequest, getAccountFromSuffixedName, getSuffixedName } from "../../services/loginService.ts";
|
||||
import {
|
||||
@ -21,6 +22,9 @@ import { ExportFlavour } from "warframe-public-export-plus";
|
||||
import { handleStoreItemAcquisition } from "../../services/purchaseService.ts";
|
||||
import { fromStoreItem, isStoreItem } from "../../services/itemDataService.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) => {
|
||||
const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
|
||||
@ -31,11 +35,11 @@ export const inboxController: RequestHandler = async (req, res) => {
|
||||
if (deleteId) {
|
||||
if (deleteId === "DeleteAllRead") {
|
||||
await deleteAllMessagesRead(accountId);
|
||||
res.status(200).end();
|
||||
return;
|
||||
} else if (deleteId === "DeleteAllReadNonCin") {
|
||||
await deleteAllMessagesReadNonCin(accountId);
|
||||
} else {
|
||||
await deleteMessageRead(parseOid(deleteId as string));
|
||||
}
|
||||
|
||||
await deleteMessageRead(parseOid(deleteId as string));
|
||||
res.status(200).end();
|
||||
} else if (messageId) {
|
||||
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
|
||||
const parseOid = (oid: string): string => {
|
||||
if (oid[0] == "{") {
|
||||
|
||||
@ -9,6 +9,9 @@ import type { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "../../
|
||||
import { logger } from "../../utils/logger.ts";
|
||||
import { version_compare } from "../../helpers/inventoryHelpers.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) => {
|
||||
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());
|
||||
|
||||
// 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));
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
|
||||
import { getAccountForRequest } from "../../services/loginService.ts";
|
||||
import { Account, Ignore } from "../../models/loginModel.ts";
|
||||
import { Inbox } from "../../models/inboxModel.ts";
|
||||
import { Inventory } from "../../models/inventoryModels/inventoryModel.ts";
|
||||
@ -12,33 +12,44 @@ import { Leaderboard } from "../../models/leaderboardModel.ts";
|
||||
import { deleteGuild } from "../../services/guildService.ts";
|
||||
import { Friendship } from "../../models/friendModel.ts";
|
||||
import { sendWsBroadcastTo } from "../../services/wsService.ts";
|
||||
import { config } from "../../services/configService.ts";
|
||||
import { saveConfig } from "../../services/configWriterService.ts";
|
||||
|
||||
export const deleteAccountController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const account = await getAccountForRequest(req);
|
||||
|
||||
// If this account is an admin, remove it from administratorNames
|
||||
if (config.administratorNames) {
|
||||
const adminIndex = config.administratorNames.indexOf(account.DisplayName);
|
||||
if (adminIndex != -1) {
|
||||
config.administratorNames.splice(adminIndex, 1);
|
||||
await saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// If account is the founding warlord of a guild, delete that guild as well.
|
||||
const guildMember = await GuildMember.findOne({ accountId, rank: 0, status: 0 });
|
||||
const guildMember = await GuildMember.findOne({ accountId: account._id, rank: 0, status: 0 });
|
||||
if (guildMember) {
|
||||
await deleteGuild(guildMember.guildId);
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
Account.deleteOne({ _id: accountId }),
|
||||
Friendship.deleteMany({ owner: accountId }),
|
||||
Friendship.deleteMany({ friend: accountId }),
|
||||
GuildMember.deleteMany({ accountId: accountId }),
|
||||
Ignore.deleteMany({ ignorer: accountId }),
|
||||
Ignore.deleteMany({ ignoree: accountId }),
|
||||
Inbox.deleteMany({ ownerId: accountId }),
|
||||
Inventory.deleteOne({ accountOwnerId: accountId }),
|
||||
Leaderboard.deleteMany({ ownerId: accountId }),
|
||||
Loadout.deleteOne({ loadoutOwnerId: accountId }),
|
||||
PersonalRooms.deleteOne({ personalRoomsOwnerId: accountId }),
|
||||
Ship.deleteMany({ ShipOwnerId: accountId }),
|
||||
Stats.deleteOne({ accountOwnerId: accountId })
|
||||
Account.deleteOne({ _id: account._id }),
|
||||
Friendship.deleteMany({ owner: account._id }),
|
||||
Friendship.deleteMany({ friend: account._id }),
|
||||
GuildMember.deleteMany({ accountId: account._id }),
|
||||
Ignore.deleteMany({ ignorer: account._id }),
|
||||
Ignore.deleteMany({ ignoree: account._id }),
|
||||
Inbox.deleteMany({ ownerId: account._id }),
|
||||
Inventory.deleteOne({ accountOwnerId: account._id }),
|
||||
Leaderboard.deleteMany({ ownerId: account._id }),
|
||||
Loadout.deleteOne({ loadoutOwnerId: account._id }),
|
||||
PersonalRooms.deleteOne({ personalRoomsOwnerId: account._id }),
|
||||
Ship.deleteMany({ ShipOwnerId: account._id }),
|
||||
Stats.deleteOne({ accountOwnerId: account._id })
|
||||
]);
|
||||
|
||||
sendWsBroadcastTo(accountId, { logged_out: true });
|
||||
sendWsBroadcastTo(account._id.toString(), { logged_out: true });
|
||||
|
||||
res.end();
|
||||
};
|
||||
|
||||
@ -115,7 +115,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
|
||||
if (stage > 0) {
|
||||
await giveKeyChainStageTriggered(inventory, {
|
||||
KeyChain: questKey.ItemType,
|
||||
ChainStage: stage
|
||||
ChainStage: stage - 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ export interface IConfig {
|
||||
anniversary?: number;
|
||||
hallowedNightmares?: boolean;
|
||||
hallowedNightmaresRewardsOverride?: number;
|
||||
naberusNightsOverride?: boolean;
|
||||
proxyRebellion?: boolean;
|
||||
proxyRebellionRewardsOverride?: number;
|
||||
galleonOfGhouls?: number;
|
||||
|
||||
@ -6,6 +6,7 @@ import type {
|
||||
} from "../types/inventoryTypes/commonInventoryTypes.ts";
|
||||
import type { IMongoDate } from "../types/commonTypes.ts";
|
||||
import type {
|
||||
IBooster,
|
||||
IDialogueClient,
|
||||
IDialogueDatabase,
|
||||
IDialogueHistoryClient,
|
||||
@ -463,6 +464,9 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
if (client.Accolades !== undefined) {
|
||||
db.Accolades = client.Accolades;
|
||||
}
|
||||
if (client.Boosters !== undefined) {
|
||||
replaceArray<IBooster>(db.Boosters, client.Boosters);
|
||||
}
|
||||
};
|
||||
|
||||
export const importLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
import type { IMessageDatabase } from "../models/inboxModel.ts";
|
||||
import { Inbox } from "../models/inboxModel.ts";
|
||||
import { getAccountForRequest } from "./loginService.ts";
|
||||
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";
|
||||
import type { HydratedDocument, Types } from "mongoose";
|
||||
|
||||
export const getAllMessagesSorted = async (accountId: string): Promise<HydratedDocument<IMessageDatabase>[]> => {
|
||||
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 });
|
||||
};
|
||||
|
||||
export 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();
|
||||
export const deleteAllMessagesReadNonCin = async (accountId: string): Promise<void> => {
|
||||
await Inbox.deleteMany({ ownerId: accountId, r: true, cinematic: null });
|
||||
};
|
||||
|
||||
export const createMessage = async (
|
||||
|
||||
@ -331,7 +331,7 @@ export const giveKeyChainMessage = async (
|
||||
): Promise<void> => {
|
||||
const keyChainMessage = getKeyChainMessage(keyChainInfo);
|
||||
|
||||
if (questKey.Progress![0].c > 0) {
|
||||
if ((questKey.Progress?.[0]?.c ?? 0) > 0) {
|
||||
keyChainMessage.att = [];
|
||||
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 missionTypes = new Set();
|
||||
|
||||
@ -309,7 +317,7 @@ export const getSortie = (day: number): ISortie => {
|
||||
"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;
|
||||
pushTilesetModifiers(modifiers, tileset);
|
||||
|
||||
@ -361,7 +369,9 @@ export const getSortie = (day: number): ISortie => {
|
||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
||||
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
||||
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,
|
||||
Variants: selectedNodes
|
||||
};
|
||||
@ -2504,6 +2514,37 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
BonusReward: { items: ["/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"] }
|
||||
});
|
||||
}
|
||||
|
||||
const isOctober = date.getUTCMonth() == 9; // October = month index 9
|
||||
if (config.worldState?.naberusNightsOverride ?? isOctober) {
|
||||
worldState.Goals.push({
|
||||
_id: { $oid: "66fd602de1778d583419e8e7" },
|
||||
Activation: {
|
||||
$date: {
|
||||
$numberLong: config.worldState?.naberusNightsOverride
|
||||
? "1727881200000"
|
||||
: Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1).toString()
|
||||
}
|
||||
},
|
||||
Expiry: {
|
||||
$date: {
|
||||
$numberLong: config.worldState?.naberusNightsOverride
|
||||
? "2000000000000"
|
||||
: Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1).toString()
|
||||
}
|
||||
},
|
||||
Count: 0,
|
||||
Goal: 0,
|
||||
Success: 0,
|
||||
Personal: true,
|
||||
Desc: "/Lotus/Language/Events/HalloweenNaberusName",
|
||||
ToolTip: "/Lotus/Language/Events/HalloweenNaberusDesc",
|
||||
Icon: "/Lotus/Interface/Icons/JackOLanternColour.png",
|
||||
Tag: "DeimosHalloween",
|
||||
Node: "DeimosHub"
|
||||
});
|
||||
}
|
||||
|
||||
if (config.worldState?.bellyOfTheBeast) {
|
||||
worldState.Goals.push({
|
||||
_id: { $oid: "67a5035c2a198564d62e165e" },
|
||||
|
||||
@ -1207,6 +1207,14 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label class="form-label" for="worldState.naberusNightsOverride" data-loc="worldState_naberusNights"></label>
|
||||
<select class="form-control" id="worldState.naberusNightsOverride" data-default="null">
|
||||
<option value="null" data-loc="normal"></option>
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2 d-flex gap-2">
|
||||
<div class="flex-fill">
|
||||
<label class="form-label" for="worldState.proxyRebellion" data-loc="worldState_proxyRebellion"></label>
|
||||
|
||||
@ -193,7 +193,7 @@ dict = {
|
||||
cheats_skipTutorial: `Tutorial überspringen`,
|
||||
cheats_skipAllDialogue: `Alle Dialoge überspringen`,
|
||||
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_ok: `Erfolgreich. Bitte beachte, dass du ein Dojo/Relais besuchen oder dich neu einloggen musst, damit die Sternenkarte aktualisiert wird.`,
|
||||
cheats_infiniteCredits: `Unendlich Credits`,
|
||||
@ -227,7 +227,7 @@ dict = {
|
||||
cheats_baroFullyStocked: `Baro hat volles Inventar`,
|
||||
cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
|
||||
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_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`,
|
||||
cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`,
|
||||
@ -284,6 +284,7 @@ dict = {
|
||||
worldState_hallowedFlame: `Geweihte Flamme`,
|
||||
worldState_hallowedNightmares: `Geweihte Albträume`,
|
||||
worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`,
|
||||
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
|
||||
worldState_proxyRebellion: `Proxy-Rebellion`,
|
||||
worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`,
|
||||
worldState_bellyOfTheBeast: `Das Innere der Bestie`,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
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_addButton: `Add`,
|
||||
general_setButton: `Set`,
|
||||
@ -192,7 +192,7 @@ dict = {
|
||||
cheats_skipTutorial: `Skip Tutorial`,
|
||||
cheats_skipAllDialogue: `Skip All Dialogue`,
|
||||
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_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`,
|
||||
@ -226,7 +226,7 @@ dict = {
|
||||
cheats_baroFullyStocked: `Baro Fully Stocked`,
|
||||
cheats_syndicateMissionsRepeatable: `Syndicate Missions Repeatable`,
|
||||
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_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
|
||||
cheats_noResourceExtractorDronesDamage: `No Resource Extractor Drones Damage`,
|
||||
@ -283,6 +283,7 @@ dict = {
|
||||
worldState_hallowedFlame: `Hallowed Flame`,
|
||||
worldState_hallowedNightmares: `Hallowed Nightmares`,
|
||||
worldState_hallowedNightmaresRewards: `Hallowed Nightmares Rewards`,
|
||||
worldState_naberusNights: `Nights of Naberus`,
|
||||
worldState_proxyRebellion: `Proxy Rebellion`,
|
||||
worldState_proxyRebellionRewards: `Proxy Rebellion Rewards`,
|
||||
worldState_bellyOfTheBeast: `Belly of the Beast`,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Spanish translation by hxedcl
|
||||
// Spanish translation by hxedcl, Slayer55555
|
||||
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_inventoryUpdateNoteGameWs: `Nota: Puede que necesites reabrir cualquier menú en el que te encuentres para que los cambios se reflejen.`,
|
||||
@ -284,6 +284,7 @@ dict = {
|
||||
worldState_hallowedFlame: `Llama Sagrada`,
|
||||
worldState_hallowedNightmares: `Pesadillas Sagradas`,
|
||||
worldState_hallowedNightmaresRewards: `Recompensas de Pesadillas Sagradas`,
|
||||
worldState_naberusNights: `Noches de Naberus`,
|
||||
worldState_proxyRebellion: `Rebelión Proxy`,
|
||||
worldState_proxyRebellionRewards: `Recompensas de Rebelión Proxy`,
|
||||
worldState_bellyOfTheBeast: `Vientre de la Bestia`,
|
||||
|
||||
@ -284,6 +284,7 @@ dict = {
|
||||
worldState_hallowedFlame: `Flamme Hantée`,
|
||||
worldState_hallowedNightmares: `Cauchemars Hantés`,
|
||||
worldState_hallowedNightmaresRewards: `Récompenses Flamme Hantée Cauchemar`,
|
||||
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
|
||||
worldState_proxyRebellion: `Rébellion Proxy`,
|
||||
worldState_proxyRebellionRewards: `Récompenses Rébellion Proxy`,
|
||||
worldState_bellyOfTheBeast: `Ventre de la Bête`,
|
||||
|
||||
@ -284,6 +284,7 @@ dict = {
|
||||
worldState_hallowedFlame: `Священное пламя`,
|
||||
worldState_hallowedNightmares: `Священные кошмары`,
|
||||
worldState_hallowedNightmaresRewards: `Награды Священных кошмаров`,
|
||||
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
|
||||
worldState_proxyRebellion: `Восстание роботов`,
|
||||
worldState_proxyRebellionRewards: `Награды Восстания роботов`,
|
||||
worldState_bellyOfTheBeast: `Чрево зверя`,
|
||||
|
||||
@ -284,6 +284,7 @@ dict = {
|
||||
worldState_hallowedFlame: `Священне полум'я`,
|
||||
worldState_hallowedNightmares: `Священні жахіття`,
|
||||
worldState_hallowedNightmaresRewards: `Нагороди Священних жахіть`,
|
||||
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
|
||||
worldState_proxyRebellion: `Повстання роботів`,
|
||||
worldState_proxyRebellionRewards: `Нагороди Повстання роботів`,
|
||||
worldState_bellyOfTheBeast: `У лігві звіра`,
|
||||
|
||||
@ -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 = {
|
||||
general_inventoryUpdateNote: `注意: 要在游戏中查看更改,您需要重新同步库存,例如使用客户端的 /sync 命令,访问道场/中继站或重新登录.`,
|
||||
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_skipAllDialogue: `跳过所有对话`,
|
||||
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_ok: `操作成功.请注意,您需要进入道场/中继站或重新登录以刷新星图数据.`,
|
||||
cheats_infiniteCredits: `无限现金`,
|
||||
@ -227,7 +227,7 @@ dict = {
|
||||
cheats_baroFullyStocked: `虚空商人贩卖所有商品`,
|
||||
cheats_syndicateMissionsRepeatable: `集团任务可重复完成`,
|
||||
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_instantResourceExtractorDrones: `资源无人机即时完成`,
|
||||
cheats_noResourceExtractorDronesDamage: `资源无人机不会损毁`,
|
||||
@ -284,6 +284,7 @@ dict = {
|
||||
worldState_hallowedFlame: `万圣之焰`,
|
||||
worldState_hallowedNightmares: `万圣噩梦`,
|
||||
worldState_hallowedNightmaresRewards: `万圣噩梦奖励设置`,
|
||||
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
|
||||
worldState_proxyRebellion: `机械叛乱`,
|
||||
worldState_proxyRebellionRewards: `机械叛乱奖励设置`,
|
||||
worldState_bellyOfTheBeast: `兽之腹`,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user