Compare commits

...

21 Commits

Author SHA1 Message Date
e67ef63b77 fix: avoid using assassination node for an earlier sortie mission (#2838)
Closes #2837

Reviewed-on: OpenWF/SpaceNinjaServer#2838
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-04 04:18:21 -07:00
5772ebe746 feat(import): boosters (#2836)
Reviewed-on: OpenWF/SpaceNinjaServer#2836
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-03 06:46:07 -07:00
0136e4d152 chore(webui): clarify /sync command goes into chat (#2835)
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2835
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: LoseFace <loseface@noreply.localhost>
Co-committed-by: LoseFace <loseface@noreply.localhost>
2025-10-03 06:45:57 -07:00
8b3ee4b4f5 chore: allow sortie image randomisation for most tilesets (#2834)
This should reduce the impact while we investigate #2833

Reviewed-on: OpenWF/SpaceNinjaServer#2834
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-10-02 05:27:56 -07:00
6e8800f048 chore(webui): fix typos (#2832)
also updated author credits

Reviewed-on: OpenWF/SpaceNinjaServer#2832
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Animan8000 <animan8000@noreply.localhost>
Co-committed-by: Animan8000 <animan8000@noreply.localhost>
2025-10-01 01:23:08 -07:00
d65a667acd fix: ensure sorties show 'correct' image for corpus ice planet tileset (#2831)
Reviewed-on: OpenWF/SpaceNinjaServer#2831
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-30 00:00:13 -07:00
c6a3e86d2b fix(webui): invoke giveKeyChainStageTriggered for new stage (#2830)
Previously, this caused the old stage to just be reinitiated so we never went backwards.

Closes #2829

Reviewed-on: OpenWF/SpaceNinjaServer#2830
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 23:59:35 -07:00
a8e41c95e7 chore: move createNewEventMessages from inboxService to inboxController (#2828)
This function wasn't used anywhere else and caused a recursive include in inboxService.

Reviewed-on: OpenWF/SpaceNinjaServer#2828
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 23:59:26 -07:00
9426359370 feat: Nights of Naberus (#2817)
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2817
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: Gian <gianplu55@gmail.com>
Co-committed-by: Gian <gianplu55@gmail.com>
2025-09-29 23:59:17 -07:00
e5247700df fix: use safe navigation to check for replay in giveKeyChainMessage (#2826)
Reviewed-on: OpenWF/SpaceNinjaServer#2826
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 02:00:32 -07:00
1c3f1e2276 feat: DeleteAllReadNonCin (#2824)
Closes #2822

Reviewed-on: OpenWF/SpaceNinjaServer#2824
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 02:00:17 -07:00
7710e7c13f feat: inbox message for relics cracked during an unfinished mission (#2823)
Closes #2821

Reviewed-on: OpenWF/SpaceNinjaServer#2823
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-29 02:00:05 -07:00
a64c5ea3c1 chore(webui): remove administratorNames entry when deleting account (#2820)
Closes #2819

Reviewed-on: OpenWF/SpaceNinjaServer#2820
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-28 01:10:45 -07:00
17e1eb86dd fix(webui): don't send off 2 addXp requests at once (#2815)
One would likely fail due to Mongoose's array versioning

Closes #2811

Reviewed-on: OpenWF/SpaceNinjaServer#2815
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-27 03:23:53 -07:00
de9dfb3d71 fix: show endless relic rewards in EOM screen (#2813)
Closes #2812

Reviewed-on: OpenWF/SpaceNinjaServer#2813
Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com>
Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
2025-09-26 04:42:08 -07:00
fc38f818dd feat: nemesis henchmen kills multiplier cheat (#2806)
Co-authored-by: AlexisinGit <136088944+AlexisinGit@users.noreply.github.com>
Reviewed-on: OpenWF/SpaceNinjaServer#2806
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AlexisinGit <alexisingit@noreply.localhost>
Co-committed-by: AlexisinGit <alexisingit@noreply.localhost>
2025-09-26 04:41:54 -07:00
e76f08db89 chore(webui): update to Spanish translation (#2814)
Reviewed-on: OpenWF/SpaceNinjaServer#2814
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-09-25 10:51:44 -07:00
7bcb5f21ce chore(webui): unify Invigoration code (#2809)
Reviewed-on: OpenWF/SpaceNinjaServer#2809
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-25 10:21:04 -07:00
3641d63f6f chore(webui): update to Spanish translation (#2810)
Reviewed-on: OpenWF/SpaceNinjaServer#2810
Co-authored-by: hxedcl <hxedcl@noreply.localhost>
Co-committed-by: hxedcl <hxedcl@noreply.localhost>
2025-09-24 23:49:10 -07:00
71c4835a69 chore(webui): unify Boosters code (#2808)
Reviewed-on: OpenWF/SpaceNinjaServer#2808
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-24 08:41:35 -07:00
86a63ace41 chore(webui): adjust checks for guild view requests (#2807)
Reviewed-on: OpenWF/SpaceNinjaServer#2807
Reviewed-by: Sainan <63328889+sainan@users.noreply.github.com>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
2025-09-24 08:41:04 -07:00
33 changed files with 688 additions and 501 deletions

View File

@ -38,6 +38,7 @@
"anniversary": null, "anniversary": null,
"hallowedNightmares": false, "hallowedNightmares": false,
"hallowedNightmaresRewardsOverride": 0, "hallowedNightmaresRewardsOverride": 0,
"naberusNightsOverride": null,
"proxyRebellion": false, "proxyRebellion": false,
"proxyRebellionRewardsOverride": 0, "proxyRebellionRewardsOverride": 0,
"galleonOfGhouls": 0, "galleonOfGhouls": 0,

View File

@ -11,7 +11,11 @@ export const getVoidProjectionRewardsController: RequestHandler = async (req, re
if (data.ParticipantInfo.QualifiesForReward && !data.ParticipantInfo.HaveRewardResponse) { if (data.ParticipantInfo.QualifiesForReward && !data.ParticipantInfo.HaveRewardResponse) {
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);
await crackRelic(inventory, data.ParticipantInfo); const reward = await crackRelic(inventory, data.ParticipantInfo);
if (!inventory.MissionRelicRewards || inventory.MissionRelicRewards.length >= data.CurrentWave) {
inventory.MissionRelicRewards = [];
}
inventory.MissionRelicRewards.push({ ItemType: reward.type, ItemCount: reward.itemCount });
await inventory.save(); await inventory.save();
} }

View File

@ -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] == "{") {

View File

@ -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));
}; };

View File

@ -149,7 +149,10 @@ export const nemesisController: RequestHandler = async (req, res) => {
break; break;
} }
} }
inventory.Nemesis!.HenchmenKilled += antivirusGain; const antivirusGainMultiplier = (
await getInventory(account._id.toString(), "nemesisAntivirusGainMultiplier")
).nemesisAntivirusGainMultiplier;
inventory.Nemesis!.HenchmenKilled += antivirusGain * (antivirusGainMultiplier ?? 1);
if (inventory.Nemesis!.HenchmenKilled >= 100) { if (inventory.Nemesis!.HenchmenKilled >= 100) {
inventory.Nemesis!.HenchmenKilled = 100; inventory.Nemesis!.HenchmenKilled = 100;

View File

@ -1,16 +1,16 @@
import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts"; import { getInventory } from "../../services/inventoryService.ts";
import type { RequestHandler } from "express"; import type { RequestHandler } from "express";
import { hasAccessToDojo, getGuildForRequestEx, hasGuildPermission } from "../../services/guildService.ts"; import { getGuildForRequestEx, hasGuildPermission } from "../../services/guildService.ts";
import { GuildPermission } from "../../types/guildTypes.ts"; import { GuildPermission } from "../../types/guildTypes.ts";
import type { ITypeCount } from "../../types/commonTypes.ts"; import type { ITypeCount } from "../../types/commonTypes.ts";
export const addVaultDecoRecipeController: RequestHandler = async (req, res) => { export const addVaultDecoRecipeController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const requests = req.body as ITypeCount[]; const requests = req.body as ITypeCount[];
const inventory = await getInventory(accountId, "LevelKeys GuildId"); const inventory = await getInventory(accountId, "GuildId");
const guild = await getGuildForRequestEx(req, inventory); const guild = await getGuildForRequestEx(req, inventory);
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Architect))) { if (!(await hasGuildPermission(guild, accountId, GuildPermission.Architect))) {
res.status(400).send("-1").end(); res.status(400).send("-1").end();
return; return;
} }

View File

@ -1,5 +1,5 @@
import type { RequestHandler } from "express"; 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 { Account, Ignore } from "../../models/loginModel.ts";
import { Inbox } from "../../models/inboxModel.ts"; import { Inbox } from "../../models/inboxModel.ts";
import { Inventory } from "../../models/inventoryModels/inventoryModel.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 { deleteGuild } from "../../services/guildService.ts";
import { Friendship } from "../../models/friendModel.ts"; import { Friendship } from "../../models/friendModel.ts";
import { sendWsBroadcastTo } from "../../services/wsService.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) => { 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. // 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) { if (guildMember) {
await deleteGuild(guildMember.guildId); await deleteGuild(guildMember.guildId);
} }
await Promise.all([ await Promise.all([
Account.deleteOne({ _id: accountId }), Account.deleteOne({ _id: account._id }),
Friendship.deleteMany({ owner: accountId }), Friendship.deleteMany({ owner: account._id }),
Friendship.deleteMany({ friend: accountId }), Friendship.deleteMany({ friend: account._id }),
GuildMember.deleteMany({ accountId: accountId }), GuildMember.deleteMany({ accountId: account._id }),
Ignore.deleteMany({ ignorer: accountId }), Ignore.deleteMany({ ignorer: account._id }),
Ignore.deleteMany({ ignoree: accountId }), Ignore.deleteMany({ ignoree: account._id }),
Inbox.deleteMany({ ownerId: accountId }), Inbox.deleteMany({ ownerId: account._id }),
Inventory.deleteOne({ accountOwnerId: accountId }), Inventory.deleteOne({ accountOwnerId: account._id }),
Leaderboard.deleteMany({ ownerId: accountId }), Leaderboard.deleteMany({ ownerId: account._id }),
Loadout.deleteOne({ loadoutOwnerId: accountId }), Loadout.deleteOne({ loadoutOwnerId: account._id }),
PersonalRooms.deleteOne({ personalRoomsOwnerId: accountId }), PersonalRooms.deleteOne({ personalRoomsOwnerId: account._id }),
Ship.deleteMany({ ShipOwnerId: accountId }), Ship.deleteMany({ ShipOwnerId: account._id }),
Stats.deleteOne({ accountOwnerId: accountId }) Stats.deleteOne({ accountOwnerId: account._id })
]); ]);
sendWsBroadcastTo(accountId, { logged_out: true }); sendWsBroadcastTo(account._id.toString(), { logged_out: true });
res.end(); res.end();
}; };

View File

@ -1,36 +0,0 @@
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import type { RequestHandler } from "express";
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
const DEFAULT_UPGRADE_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
export const editSuitInvigorationUpgradeController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const { oid, data } = req.body as {
oid: string;
data?: {
DefensiveUpgrade: string;
OffensiveUpgrade: string;
UpgradesExpiry?: number;
};
};
const inventory = await getInventory(accountId);
const suit = inventory.Suits.id(oid)!;
if (data) {
suit.DefensiveUpgrade = data.DefensiveUpgrade;
suit.OffensiveUpgrade = data.OffensiveUpgrade;
if (data.UpgradesExpiry) {
suit.UpgradesExpiry = new Date(data.UpgradesExpiry);
} else {
suit.UpgradesExpiry = new Date(Date.now() + DEFAULT_UPGRADE_EXPIRY_MS);
}
} else {
suit.DefensiveUpgrade = undefined;
suit.OffensiveUpgrade = undefined;
suit.UpgradesExpiry = undefined;
}
await inventory.save();
res.end();
broadcastInventoryUpdate(req);
};

View File

@ -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
}); });
} }
} }

View File

@ -3,12 +3,19 @@ import { getAccountIdForRequest } from "../../services/loginService.ts";
import { sendWsBroadcastTo } from "../../services/wsService.ts"; import { sendWsBroadcastTo } from "../../services/wsService.ts";
import type { IAccountCheats } from "../../types/inventoryTypes/inventoryTypes.ts"; import type { IAccountCheats } from "../../types/inventoryTypes/inventoryTypes.ts";
import type { RequestHandler } from "express"; import type { RequestHandler } from "express";
import { logger } from "../../utils/logger.ts";
export const setAccountCheatController: RequestHandler = async (req, res) => { export const setAccountCheatController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const payload = req.body as ISetAccountCheatRequest; const payload = req.body as ISetAccountCheatRequest;
const inventory = await getInventory(accountId, payload.key); const inventory = await getInventory(accountId, payload.key);
inventory[payload.key] = payload.value;
if (payload.value == undefined) {
logger.warn(`Aborting setting ${payload.key} as undefined!`);
return;
}
inventory[payload.key] = payload.value as never;
await inventory.save(); await inventory.save();
res.end(); res.end();
if (["infiniteCredits", "infinitePlatinum", "infiniteEndo", "infiniteRegalAya"].indexOf(payload.key) != -1) { if (["infiniteCredits", "infinitePlatinum", "infiniteEndo", "infiniteRegalAya"].indexOf(payload.key) != -1) {
@ -18,5 +25,5 @@ export const setAccountCheatController: RequestHandler = async (req, res) => {
interface ISetAccountCheatRequest { interface ISetAccountCheatRequest {
key: keyof IAccountCheats; key: keyof IAccountCheats;
value: boolean; value: IAccountCheats[keyof IAccountCheats];
} }

View File

@ -1,44 +1,19 @@
import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts"; import { getInventory } from "../../services/inventoryService.ts";
import type { RequestHandler } from "express"; import type { RequestHandler } from "express";
import { ExportBoosters } from "warframe-public-export-plus"; import type { IBooster } from "../../types/inventoryTypes/inventoryTypes.ts";
import { broadcastInventoryUpdate } from "../../services/wsService.ts"; import { broadcastInventoryUpdate } from "../../services/wsService.ts";
const I32_MAX = 0x7fffffff;
export const setBoosterController: RequestHandler = async (req, res) => { export const setBoosterController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const requests = req.body as { ItemType: string; ExpiryDate: number }[]; const requests = req.body as IBooster[];
const inventory = await getInventory(accountId, "Boosters"); const inventory = await getInventory(accountId, "Boosters");
const boosters = inventory.Boosters; for (const request of requests) {
if ( const index = inventory.Boosters.findIndex(item => item.ItemType === request.ItemType);
requests.some(request => {
if (typeof request.ItemType !== "string") return true;
if (Object.entries(ExportBoosters).find(([_, item]) => item.typeName === request.ItemType) === undefined)
return true;
if (typeof request.ExpiryDate !== "number") return true;
if (request.ExpiryDate < 0 || request.ExpiryDate > I32_MAX) return true;
return false;
})
) {
res.status(400).send("Invalid ItemType provided.");
return;
}
const now = Math.trunc(Date.now() / 1000);
for (const { ItemType, ExpiryDate } of requests) {
if (ExpiryDate <= now) {
// remove expired boosters
const index = boosters.findIndex(item => item.ItemType === ItemType);
if (index !== -1) { if (index !== -1) {
boosters.splice(index, 1); inventory.Boosters[index].ExpiryDate = request.ExpiryDate;
}
} else { } else {
const boosterItem = boosters.find(item => item.ItemType === ItemType); inventory.Boosters.push(request);
if (boosterItem) {
boosterItem.ExpiryDate = ExpiryDate;
} else {
boosters.push({ ItemType, ExpiryDate });
}
} }
} }
await inventory.save(); await inventory.save();

View File

@ -1,5 +1,5 @@
import { GuildMember } from "../../models/guildModel.ts"; import { GuildMember } from "../../models/guildModel.ts";
import { getGuildForRequestEx, hasAccessToDojo } from "../../services/guildService.ts"; import { getGuildForRequestEx } from "../../services/guildService.ts";
import { getInventory } from "../../services/inventoryService.ts"; import { getInventory } from "../../services/inventoryService.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts"; import { getAccountIdForRequest } from "../../services/loginService.ts";
import type { IGuildCheats } from "../../types/guildTypes.ts"; import type { IGuildCheats } from "../../types/guildTypes.ts";
@ -8,12 +8,12 @@ import type { RequestHandler } from "express";
export const setGuildCheatController: RequestHandler = async (req, res) => { export const setGuildCheatController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const payload = req.body as ISetGuildCheatRequest; const payload = req.body as ISetGuildCheatRequest;
const inventory = await getInventory(accountId, `${payload.key} GuildId LevelKeys`); const inventory = await getInventory(accountId, `GuildId`);
const guild = await getGuildForRequestEx(req, inventory); const guild = await getGuildForRequestEx(req, inventory);
const member = await GuildMember.findOne({ accountId: accountId, guildId: guild._id }); const member = await GuildMember.findOne({ accountId: accountId, guildId: guild._id });
if (member) { if (member) {
if (!hasAccessToDojo(inventory) || member.rank > 1) { if (member.rank > 1) {
res.end(); res.end();
return; return;
} }

View File

@ -0,0 +1,27 @@
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import type { RequestHandler } from "express";
import { broadcastInventoryUpdate } from "../../services/wsService.ts";
export const setInvigorationController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const request = req.body as ISetInvigorationRequest;
const inventory = await getInventory(accountId, "Suits");
const suit = inventory.Suits.id(request.oid);
if (suit) {
const hasUpgrades = request.DefensiveUpgrade && request.OffensiveUpgrade && request.UpgradesExpiry;
suit.DefensiveUpgrade = hasUpgrades ? request.DefensiveUpgrade : undefined;
suit.OffensiveUpgrade = hasUpgrades ? request.OffensiveUpgrade : undefined;
suit.UpgradesExpiry = hasUpgrades ? new Date(request.UpgradesExpiry) : undefined;
await inventory.save();
broadcastInventoryUpdate(req);
}
res.end();
};
interface ISetInvigorationRequest {
oid: string;
DefensiveUpgrade: string;
OffensiveUpgrade: string;
UpgradesExpiry: number;
}

View File

@ -2,7 +2,6 @@ import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts"; import { getInventory } from "../../services/inventoryService.ts";
import type { RequestHandler } from "express"; import type { RequestHandler } from "express";
import { import {
hasAccessToDojo,
getGuildForRequestEx, getGuildForRequestEx,
setGuildTechLogState, setGuildTechLogState,
processFundedGuildTechProject, processFundedGuildTechProject,
@ -19,9 +18,9 @@ import { GuildMember } from "../../models/guildModel.ts";
export const addTechProjectController: RequestHandler = async (req, res) => { export const addTechProjectController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const requests = req.body as ITechProjectRequest[]; const requests = req.body as ITechProjectRequest[];
const inventory = await getInventory(accountId, "LevelKeys GuildId"); const inventory = await getInventory(accountId, "GuildId");
const guild = await getGuildForRequestEx(req, inventory); const guild = await getGuildForRequestEx(req, inventory);
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) { if (!(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
res.status(400).send("-1").end(); res.status(400).send("-1").end();
return; return;
} }
@ -54,9 +53,9 @@ export const addTechProjectController: RequestHandler = async (req, res) => {
export const removeTechProjectController: RequestHandler = async (req, res) => { export const removeTechProjectController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const requests = req.body as ITechProjectRequest[]; const requests = req.body as ITechProjectRequest[];
const inventory = await getInventory(accountId, "LevelKeys GuildId"); const inventory = await getInventory(accountId, "GuildId");
const guild = await getGuildForRequestEx(req, inventory); const guild = await getGuildForRequestEx(req, inventory);
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) { if (!(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
res.status(400).send("-1").end(); res.status(400).send("-1").end();
return; return;
} }
@ -74,13 +73,13 @@ export const removeTechProjectController: RequestHandler = async (req, res) => {
export const fundTechProjectController: RequestHandler = async (req, res) => { export const fundTechProjectController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const requests = req.body as ITechProjectRequest[]; const requests = req.body as ITechProjectRequest[];
const inventory = await getInventory(accountId, "LevelKeys GuildId"); const inventory = await getInventory(accountId, "GuildId");
const guild = await getGuildForRequestEx(req, inventory); const guild = await getGuildForRequestEx(req, inventory);
const guildMember = (await GuildMember.findOne( const guildMember = (await GuildMember.findOne(
{ accountId, guildId: guild._id }, { accountId, guildId: guild._id },
"RegularCreditsContributed MiscItemsContributed" "RegularCreditsContributed MiscItemsContributed"
))!; ))!;
if (!hasAccessToDojo(inventory)) { if (!(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
res.status(400).send("-1").end(); res.status(400).send("-1").end();
return; return;
} }
@ -105,9 +104,9 @@ export const fundTechProjectController: RequestHandler = async (req, res) => {
export const completeTechProjectsController: RequestHandler = async (req, res) => { export const completeTechProjectsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const requests = req.body as ITechProjectRequest[]; const requests = req.body as ITechProjectRequest[];
const inventory = await getInventory(accountId, "LevelKeys GuildId"); const inventory = await getInventory(accountId, "GuildId");
const guild = await getGuildForRequestEx(req, inventory); const guild = await getGuildForRequestEx(req, inventory);
if (!hasAccessToDojo(inventory)) { if (!(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
res.status(400).send("-1").end(); res.status(400).send("-1").end();
return; return;
} }

View File

@ -54,6 +54,9 @@ export const crackRelic = async (
(await handleStoreItemAcquisition(reward.type, inventory, reward.itemCount)).InventoryChanges (await handleStoreItemAcquisition(reward.type, inventory, reward.itemCount)).InventoryChanges
); );
// Client has picked its own reward (for lack of choice)
participant.ChosenRewardOwner = participant.AccountId;
return reward; return reward;
}; };

View File

@ -1462,11 +1462,20 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
flawlessRelicsAlwaysGiveSilverReward: Boolean, flawlessRelicsAlwaysGiveSilverReward: Boolean,
radiantRelicsAlwaysGiveGoldReward: Boolean, radiantRelicsAlwaysGiveGoldReward: Boolean,
disableDailyTribute: Boolean, disableDailyTribute: Boolean,
nemesisHenchmenKillsMultiplierGrineer: Number,
nemesisHenchmenKillsMultiplierCorpus: Number,
nemesisAntivirusGainMultiplier: Number,
nemesisHintProgressMultiplierGrineer: Number,
nemesisHintProgressMultiplierCorpus: Number,
nemesisExtraWeapon: Number,
SubscribedToEmails: { type: Number, default: 0 }, SubscribedToEmails: { type: Number, default: 0 },
SubscribedToEmailsPersonalized: { type: Number, default: 0 }, SubscribedToEmailsPersonalized: { type: Number, default: 0 },
RewardSeed: BigInt, RewardSeed: BigInt,
// Temporary data so we can show all relic rewards from an endless mission at EOM
MissionRelicRewards: { type: [typeCountSchema], default: undefined },
//Credit //Credit
RegularCredits: { type: Number, default: 0 }, RegularCredits: { type: Number, default: 0 },
//Platinum //Platinum
@ -1835,6 +1844,7 @@ inventorySchema.set("toJSON", {
delete returnedObject._id; delete returnedObject._id;
delete returnedObject.__v; delete returnedObject.__v;
delete returnedObject.accountOwnerId; delete returnedObject.accountOwnerId;
delete returnedObject.MissionRelicRewards;
const inventoryDatabase = returnedObject as Partial<IInventoryDatabase>; const inventoryDatabase = returnedObject as Partial<IInventoryDatabase>;
const inventoryResponse = returnedObject as IInventoryClient; const inventoryResponse = returnedObject as IInventoryClient;

View File

@ -43,7 +43,7 @@ import { setBoosterController } from "../controllers/custom/setBoosterController
import { updateFingerprintController } from "../controllers/custom/updateFingerprintController.ts"; import { updateFingerprintController } from "../controllers/custom/updateFingerprintController.ts";
import { unlockLevelCapController } from "../controllers/custom/unlockLevelCapController.ts"; import { unlockLevelCapController } from "../controllers/custom/unlockLevelCapController.ts";
import { changeModularPartsController } from "../controllers/custom/changeModularPartsController.ts"; import { changeModularPartsController } from "../controllers/custom/changeModularPartsController.ts";
import { editSuitInvigorationUpgradeController } from "../controllers/custom/editSuitInvigorationUpgradeController.ts"; import { setInvigorationController } from "../controllers/custom/setInvigorationController.ts";
import { setAccountCheatController } from "../controllers/custom/setAccountCheatController.ts"; import { setAccountCheatController } from "../controllers/custom/setAccountCheatController.ts";
import { setGuildCheatController } from "../controllers/custom/setGuildCheatController.ts"; import { setGuildCheatController } from "../controllers/custom/setGuildCheatController.ts";
@ -92,7 +92,7 @@ customRouter.post("/setBooster", setBoosterController);
customRouter.post("/updateFingerprint", updateFingerprintController); customRouter.post("/updateFingerprint", updateFingerprintController);
customRouter.post("/unlockLevelCap", unlockLevelCapController); customRouter.post("/unlockLevelCap", unlockLevelCapController);
customRouter.post("/changeModularParts", changeModularPartsController); customRouter.post("/changeModularParts", changeModularPartsController);
customRouter.post("/editSuitInvigorationUpgrade", editSuitInvigorationUpgradeController); customRouter.post("/setInvigoration", setInvigorationController);
customRouter.post("/setAccountCheat", setAccountCheatController); customRouter.post("/setAccountCheat", setAccountCheatController);
customRouter.post("/setGuildCheat", setGuildCheatController); customRouter.post("/setGuildCheat", setGuildCheatController);

View File

@ -48,6 +48,7 @@ export interface IConfig {
anniversary?: number; anniversary?: number;
hallowedNightmares?: boolean; hallowedNightmares?: boolean;
hallowedNightmaresRewardsOverride?: number; hallowedNightmaresRewardsOverride?: number;
naberusNightsOverride?: boolean;
proxyRebellion?: boolean; proxyRebellion?: boolean;
proxyRebellionRewardsOverride?: number; proxyRebellionRewardsOverride?: number;
galleonOfGhouls?: number; galleonOfGhouls?: number;

View File

@ -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 => {

View File

@ -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 (

View File

@ -210,10 +210,29 @@ export const addMissionInventoryUpdates = async (
inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards; inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
} }
if (inventoryUpdates.RewardInfo.NemesisHenchmenKills && inventory.Nemesis) { if (inventoryUpdates.RewardInfo.NemesisHenchmenKills && inventory.Nemesis) {
inventory.Nemesis.HenchmenKilled += inventoryUpdates.RewardInfo.NemesisHenchmenKills; let HenchmenKilledMultiplier = 1;
switch (inventory.Nemesis.Faction) {
case "FC_GRINEER":
HenchmenKilledMultiplier = inventory.nemesisHenchmenKillsMultiplierGrineer ?? 1;
break;
case "FC_CORPUS":
HenchmenKilledMultiplier = inventory.nemesisHenchmenKillsMultiplierCorpus ?? 1;
break;
}
inventory.Nemesis.HenchmenKilled +=
inventoryUpdates.RewardInfo.NemesisHenchmenKills * HenchmenKilledMultiplier;
} }
if (inventoryUpdates.RewardInfo.NemesisHintProgress && inventory.Nemesis) { if (inventoryUpdates.RewardInfo.NemesisHintProgress && inventory.Nemesis) {
inventory.Nemesis.HintProgress += inventoryUpdates.RewardInfo.NemesisHintProgress; let HintProgressMultiplier = 1;
switch (inventory.Nemesis.Faction) {
case "FC_GRINEER":
HintProgressMultiplier = inventory.nemesisHintProgressMultiplierGrineer ?? 1;
break;
case "FC_CORPUS":
HintProgressMultiplier = inventory.nemesisHintProgressMultiplierCorpus ?? 1;
break;
}
inventory.Nemesis.HintProgress += inventoryUpdates.RewardInfo.NemesisHintProgress * HintProgressMultiplier;
if (inventory.Nemesis.Faction != "FC_INFESTATION" && inventory.Nemesis.Hints.length != 3) { if (inventory.Nemesis.Faction != "FC_INFESTATION" && inventory.Nemesis.Hints.length != 3) {
const progressNeeded = [35, 60, 100][inventory.Nemesis.Hints.length]; const progressNeeded = [35, 60, 100][inventory.Nemesis.Hints.length];
if (inventory.Nemesis.HintProgress >= progressNeeded) { if (inventory.Nemesis.HintProgress >= progressNeeded) {
@ -819,6 +838,8 @@ export const addMissionInventoryUpdates = async (
const att: string[] = []; const att: string[] = [];
let countedAtt: ITypeCount[] | undefined; let countedAtt: ITypeCount[] | undefined;
const extraWeaponCheat = inventory.nemesisExtraWeapon ?? 0; // 0 means no extra weapon and token
if (value.killed) { if (value.killed) {
if ( if (
value.weaponLoc && value.weaponLoc &&
@ -827,6 +848,20 @@ export const addMissionInventoryUpdates = async (
const weaponType = manifest.weapons[inventory.Nemesis.WeaponIdx]; const weaponType = manifest.weapons[inventory.Nemesis.WeaponIdx];
giveNemesisWeaponRecipe(inventory, weaponType, value.nemesisName, value.weaponLoc, profile); giveNemesisWeaponRecipe(inventory, weaponType, value.nemesisName, value.weaponLoc, profile);
att.push(weaponType); att.push(weaponType);
if (extraWeaponCheat >= 1) {
for (let i = 0; i < extraWeaponCheat; i++) {
const randomIndex = Math.floor(Math.random() * manifest.weapons.length);
const randomWeapon = manifest.weapons[randomIndex];
giveNemesisWeaponRecipe(
inventory,
randomWeapon,
value.nemesisName,
undefined,
profile
);
att.push(randomWeapon);
}
}
} }
//if (value.petLoc) { //if (value.petLoc) {
if (profile.petHead) { if (profile.petHead) {
@ -870,7 +905,7 @@ export const addMissionInventoryUpdates = async (
countedAtt = [ countedAtt = [
{ {
ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks", ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
ItemCount: getKillTokenRewardCount(inventory.Nemesis.fp) ItemCount: getKillTokenRewardCount(inventory.Nemesis.fp) * (extraWeaponCheat + 1)
} }
]; ];
addMiscItems(inventory, countedAtt); addMiscItems(inventory, countedAtt);
@ -1299,13 +1334,21 @@ export const addMissionRewards = async (
rngRewardCredits: inventoryChanges.RegularCredits ?? 0 rngRewardCredits: inventoryChanges.RegularCredits ?? 0
}); });
if ( if (voidTearWave && voidTearWave.Participants[0].QualifiesForReward) {
voidTearWave && if (!voidTearWave.Participants[0].HaveRewardResponse) {
voidTearWave.Participants[0].QualifiesForReward && // non-endless fissure; giving reward now
!voidTearWave.Participants[0].HaveRewardResponse
) {
const reward = await crackRelic(inventory, voidTearWave.Participants[0], inventoryChanges); const reward = await crackRelic(inventory, voidTearWave.Participants[0], inventoryChanges);
MissionRewards.push({ StoreItem: reward.type, ItemCount: reward.itemCount }); MissionRewards.push({ StoreItem: reward.type, ItemCount: reward.itemCount });
} else if (inventory.MissionRelicRewards) {
// endless fissure; already gave reward(s) but should still show in EOM screen
for (const reward of inventory.MissionRelicRewards) {
MissionRewards.push({
StoreItem: reward.ItemType,
ItemCount: reward.ItemCount
});
}
inventory.MissionRelicRewards = undefined;
}
} }
if (strippedItems) { if (strippedItems) {
@ -1400,7 +1443,9 @@ export const addMissionRewards = async (
if (inventory.Nemesis.Faction == "FC_INFESTATION") { if (inventory.Nemesis.Faction == "FC_INFESTATION") {
inventory.Nemesis.MissionCount += 1; inventory.Nemesis.MissionCount += 1;
inventory.Nemesis.HenchmenKilled = Math.min(inventory.Nemesis.HenchmenKilled + 5, 95); // 5 progress per mission until 95 let antivirusGain = 5;
antivirusGain *= inventory.nemesisAntivirusGainMultiplier ?? 1;
inventory.Nemesis.HenchmenKilled = Math.min(inventory.Nemesis.HenchmenKilled + antivirusGain, 95); // 5 progress per mission until 95
inventoryChanges.Nemesis.MissionCount ??= 0; inventoryChanges.Nemesis.MissionCount ??= 0;
inventoryChanges.Nemesis.MissionCount += 1; inventoryChanges.Nemesis.MissionCount += 1;

View File

@ -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 = [];
} }

View File

@ -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
}; };
@ -2504,6 +2514,37 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
BonusReward: { items: ["/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"] } 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) { if (config.worldState?.bellyOfTheBeast) {
worldState.Goals.push({ worldState.Goals.push({
_id: { $oid: "67a5035c2a198564d62e165e" }, _id: { $oid: "67a5035c2a198564d62e165e" },

View File

@ -55,6 +55,12 @@ export interface IAccountCheats {
flawlessRelicsAlwaysGiveSilverReward?: boolean; flawlessRelicsAlwaysGiveSilverReward?: boolean;
radiantRelicsAlwaysGiveGoldReward?: boolean; radiantRelicsAlwaysGiveGoldReward?: boolean;
disableDailyTribute?: boolean; disableDailyTribute?: boolean;
nemesisHenchmenKillsMultiplierGrineer?: number;
nemesisHenchmenKillsMultiplierCorpus?: number;
nemesisAntivirusGainMultiplier?: number;
nemesisHintProgressMultiplierGrineer?: number;
nemesisHintProgressMultiplierCorpus?: number;
nemesisExtraWeapon?: number;
} }
export interface IInventoryDatabase export interface IInventoryDatabase
@ -141,6 +147,7 @@ export interface IInventoryDatabase
LastInventorySync?: Types.ObjectId; LastInventorySync?: Types.ObjectId;
EndlessXP?: IEndlessXpProgressDatabase[]; EndlessXP?: IEndlessXpProgressDatabase[];
PersonalGoalProgress?: IGoalProgressDatabase[]; PersonalGoalProgress?: IGoalProgressDatabase[];
MissionRelicRewards?: ITypeCount[];
} }
export interface IQuestKeyDatabase { export interface IQuestKeyDatabase {

View File

@ -476,9 +476,9 @@
</div> </div>
<div class="col-lg-6"> <div class="col-lg-6">
<div class="card" style="height: 400px;"> <div class="card" style="height: 400px;">
<h5 class="card-header" data-loc="inventory_Boosters"></h5> <h5 class="card-header" data-loc="inventory_boosters"></h5>
<div class="card-body d-flex flex-column"> <div class="card-body d-flex flex-column">
<form class="input-group mb-3" onsubmit="doAcquireBoosters();return false;"> <form class="input-group mb-3" onsubmit="doAcquireBooster();return false;">
<input class="form-control" id="acquire-type-Boosters" list="datalist-Boosters" /> <input class="form-control" id="acquire-type-Boosters" list="datalist-Boosters" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button> <button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form> </form>
@ -719,12 +719,12 @@
</div> </div>
</div> </div>
<div id="edit-suit-invigorations-card" class="card mb-3 d-none"> <div id="edit-suit-invigorations-card" class="card mb-3 d-none">
<h5 class="card-header" data-loc="detailedView_suitInvigorationLabel"></h5> <h5 class="card-header" data-loc="detailedView_invigorationLabel"></h5>
<div class="card-body"> <div class="card-body">
<form onsubmit="submitSuitInvigorationUpgrade(event)"> <form onsubmit="submitInvigoration(event)">
<div class="mb-3"> <div class="mb-3">
<label for="invigoration-offensive" class="form-label" data-loc="invigorations_offensiveLabel"></label> <label for="invigoration-offensive" class="form-label" data-loc="detailedView_invigorationOffensiveLabel"></label>
<select class="form-select" id="dv-invigoration-offensive"> <select class="form-select" id="invigoration-offensive">
<option value="" data-loc="general_none"></option> <option value="" data-loc="general_none"></option>
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerStrength" data-loc="invigorations_offensive_AbilityStrength"></option> <option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerStrength" data-loc="invigorations_offensive_AbilityStrength"></option>
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerRange" data-loc="invigorations_offensive_AbilityRange"></option> <option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerRange" data-loc="invigorations_offensive_AbilityRange"></option>
@ -737,10 +737,9 @@
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationMeleeCritChance" data-loc="invigorations_offensive_MeleeCritChance"></option> <option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationMeleeCritChance" data-loc="invigorations_offensive_MeleeCritChance"></option>
</select> </select>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="invigoration-defensive" class="form-label" data-loc="invigorations_defensiveLabel"></label> <label for="invigoration-defensive" class="form-label" data-loc="detailedView_invigorationUtilityLabel"></label>
<select class="form-select" id="dv-invigoration-defensive"> <select class="form-select" id="invigoration-defensive">
<option value="" data-loc="general_none"></option> <option value="" data-loc="general_none"></option>
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationPowerEfficiency" data-loc="invigorations_utility_AbilityEfficiency"></option> <option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationPowerEfficiency" data-loc="invigorations_utility_AbilityEfficiency"></option>
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationMovementSpeed" data-loc="invigorations_utility_SprintSpeed"></option> <option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationMovementSpeed" data-loc="invigorations_utility_SprintSpeed"></option>
@ -755,15 +754,13 @@
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationEnergyRegen" data-loc="invigorations_utility_EnergyRegen"></option> <option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationEnergyRegen" data-loc="invigorations_utility_EnergyRegen"></option>
</select> </select>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="invigoration-expiry" class="form-label" data-loc="invigorations_expiryLabel"></label> <label for="invigoration-expiry" class="form-label" data-loc="detailedView_invigorationExpiryLabel"></label>
<input type="datetime-local" class="form-control" id="dv-invigoration-expiry" /> <input type="datetime-local" class="form-control" max="2038-01-19T03:14" id="invigoration-expiry" onblur="this.value=new Date(this.value)>new Date(this.max)?new Date(this.max).toISOString().slice(0,16):this.value"/>
</div> </div>
<div class="d-flex gap-2"> <div class="d-flex gap-2">
<button type="submit" class="btn btn-primary" data-loc="general_setButton"></button> <button type="submit" class="btn btn-primary" data-loc="general_setButton"></button>
<button type="button" class="btn btn-danger" onclick="clearSuitInvigorationUpgrades()" data-loc="code_remove"></button> <button type="button" class="btn btn-danger" onclick="setInvigoration()" data-loc="code_remove"></button>
</div> </div>
</form> </form>
</div> </div>
@ -1018,6 +1015,48 @@
<input class="form-check-input" type="checkbox" id="finishInvasionsInOneMission" /> <input class="form-check-input" type="checkbox" id="finishInvasionsInOneMission" />
<label class="form-check-label" for="finishInvasionsInOneMission" data-loc="cheats_finishInvasionsInOneMission"></label> <label class="form-check-label" for="finishInvasionsInOneMission" data-loc="cheats_finishInvasionsInOneMission"></label>
</div> </div>
<form class="form-group mt-2">
<label class="form-label" for="nemesisHenchmenKillsMultiplierGrineer" data-loc="cheats_nemesisHenchmenKillsMultiplierGrineer"></label>
<div class="input-group">
<input class="form-control" id="nemesisHenchmenKillsMultiplierGrineer" type="number" min="0" max="65535" data-default="1" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2">
<label class="form-label" for="nemesisHenchmenKillsMultiplierCorpus" data-loc="cheats_nemesisHenchmenKillsMultiplierCorpus"></label>
<div class="input-group">
<input class="form-control" id="nemesisHenchmenKillsMultiplierCorpus" type="number" min="0" max="65535" data-default="1" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2">
<label class="form-label" for="nemesisAntivirusGainMultiplier" data-loc="cheats_nemesisAntivirusGainMultiplier"></label>
<div class="input-group">
<input class="form-control" id="nemesisAntivirusGainMultiplier" type="number" min="0" max="65535" data-default="1" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2">
<label class="form-label" for="nemesisHintProgressMultiplierGrineer" data-loc="cheats_nemesisHintProgressMultiplierGrineer"></label>
<div class="input-group">
<input class="form-control" id="nemesisHintProgressMultiplierGrineer" type="number" min="0" max="65535" data-default="1" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2">
<label class="form-label" for="nemesisHintProgressMultiplierCorpus" data-loc="cheats_nemesisHintProgressMultiplierCorpus"></label>
<div class="input-group">
<input class="form-control" id="nemesisHintProgressMultiplierCorpus" type="number" min="0" max="65535" data-default="1" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2">
<label class="form-label" for="nemesisExtraWeapon" data-loc="cheats_nemesisExtraWeapon"></label>
<div class="input-group">
<input class="form-control" id="nemesisExtraWeapon" type="number" min="0" max="65535" data-default="0" />
<button class="btn btn-secondary" type="button" data-loc="cheats_save"></button>
</div>
</form>
<div class="mt-2 mb-2 d-flex flex-wrap gap-2"> <div class="mt-2 mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="debounce(doUnlockAllShipFeatures);" data-loc="cheats_unlockAllShipFeatures"></button> <button class="btn btn-primary" onclick="debounce(doUnlockAllShipFeatures);" data-loc="cheats_unlockAllShipFeatures"></button>
<button class="btn btn-primary" onclick="debounce(unlockAllMissions);" data-loc="cheats_unlockAllMissions"></button> <button class="btn btn-primary" onclick="debounce(unlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
@ -1036,7 +1075,7 @@
<label class="form-label" for="changeSyndicate" data-loc="cheats_changeSupportedSyndicate"></label> <label class="form-label" for="changeSyndicate" data-loc="cheats_changeSupportedSyndicate"></label>
<div class="input-group"> <div class="input-group">
<select class="form-control" id="changeSyndicate"></select> <select class="form-control" id="changeSyndicate"></select>
<button class="btn btn-primary" type="submit" data-loc="cheats_changeButton"></button> <button class="btn btn-secondary" type="submit" data-loc="cheats_changeButton"></button>
</div> </div>
</form> </form>
</div> </div>
@ -1168,6 +1207,14 @@
</select> </select>
</div> </div>
</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="form-group mt-2 d-flex gap-2">
<div class="flex-fill"> <div class="flex-fill">
<label class="form-label" for="worldState.proxyRebellion" data-loc="worldState_proxyRebellion"></label> <label class="form-label" for="worldState.proxyRebellion" data-loc="worldState_proxyRebellion"></label>

View File

@ -837,10 +837,9 @@ function updateInventory() {
a.href = "#"; a.href = "#";
a.onclick = function (event) { a.onclick = function (event) {
event.preventDefault(); event.preventDefault();
revalidateAuthz().then(() => { revalidateAuthz().then(async () => {
const promises = [];
if (item.XP < maxXP) { if (item.XP < maxXP) {
promises.push(addGearExp(category, item.ItemId.$oid, maxXP - item.XP)); await addGearExp(category, item.ItemId.$oid, maxXP - item.XP);
} }
if ("exalted" in itemMap[item.ItemType]) { if ("exalted" in itemMap[item.ItemType]) {
for (const exaltedType of itemMap[item.ItemType].exalted) { for (const exaltedType of itemMap[item.ItemType].exalted) {
@ -851,21 +850,17 @@ function updateInventory() {
const exaltedCap = const exaltedCap =
itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000; itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
if (exaltedItem.XP < exaltedCap) { if (exaltedItem.XP < exaltedCap) {
promises.push( await addGearExp(
addGearExp(
"SpecialItems", "SpecialItems",
exaltedItem.ItemId.$oid, exaltedItem.ItemId.$oid,
exaltedCap - exaltedItem.XP exaltedCap - exaltedItem.XP
)
); );
} }
} }
} }
} }
Promise.all(promises).then(() => {
updateInventory(); updateInventory();
}); });
});
}; };
a.title = loc("code_maxRank"); a.title = loc("code_maxRank");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`; a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z"/></svg>`;
@ -1007,6 +1002,67 @@ function updateInventory() {
document.getElementById("EvolutionProgress-list").appendChild(tr); document.getElementById("EvolutionProgress-list").appendChild(tr);
}); });
document.getElementById("Boosters-list").innerHTML = "";
data.Boosters.forEach(item => {
if (item.ExpiryDate < Math.floor(Date.now() / 1000)) {
// Booster has expired, skip it
return;
}
const tr = document.createElement("tr");
{
const td = document.createElement("td");
td.textContent = itemMap[item.ItemType]?.name ?? item.ItemType;
tr.appendChild(td);
}
{
const td = document.createElement("td");
td.classList = "text-end text-nowrap";
{
const form = document.createElement("form");
form.style.display = "inline-block";
form.onsubmit = function (event) {
event.preventDefault();
const maxDate = new Date(input.max);
const selectedDate = new Date(input.value);
if (selectedDate > maxDate) {
input.value = maxDate.toISOString().slice(0, 16);
}
doChangeBoosterExpiry(item.ItemType, input);
};
const input = document.createElement("input");
input.type = "datetime-local";
input.classList = "form-control form-control-sm";
input.value = formatDatetime("%Y-%m-%d %H:%M:%s", item.ExpiryDate * 1000);
input.max = "2038-01-19T03:14";
input.onblur = function () {
const maxDate = new Date(input.max);
const selectedDate = new Date(input.value);
if (selectedDate > maxDate) {
input.value = maxDate.toISOString().slice(0, 16);
}
doChangeBoosterExpiry(item.ItemType, input);
};
form.appendChild(input);
td.appendChild(form);
}
{
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
setBooster(item.ItemType, 0);
};
a.title = loc("code_remove");
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>`;
td.appendChild(a);
}
tr.appendChild(td);
}
document.getElementById("Boosters-list").appendChild(tr);
});
document.getElementById("FlavourItems-list").innerHTML = ""; document.getElementById("FlavourItems-list").innerHTML = "";
data.FlavourItems.forEach(item => { data.FlavourItems.forEach(item => {
const datalist = document.getElementById("datalist-FlavourItems"); const datalist = document.getElementById("datalist-FlavourItems");
@ -1452,12 +1508,13 @@ function updateInventory() {
document.getElementById("crystals-list").appendChild(tr); document.getElementById("crystals-list").appendChild(tr);
}); });
{
document.getElementById("edit-suit-invigorations-card").classList.remove("d-none"); document.getElementById("edit-suit-invigorations-card").classList.remove("d-none");
const { OffensiveUpgrade, DefensiveUpgrade, UpgradesExpiry } = document.getElementById("invigoration-offensive").value = item.OffensiveUpgrade || "";
suitInvigorationUpgradeData(item); document.getElementById("invigoration-defensive").value = item.DefensiveUpgrade || "";
document.getElementById("dv-invigoration-offensive").value = OffensiveUpgrade; document.getElementById("invigoration-expiry").value =
document.getElementById("dv-invigoration-defensive").value = DefensiveUpgrade; formatDatetime("%Y-%m-%d %H:%M", Number(item.UpgradesExpiry?.$date.$numberLong)) || "";
document.getElementById("dv-invigoration-expiry").value = UpgradesExpiry; }
{ {
document.getElementById("loadout-card").classList.remove("d-none"); document.getElementById("loadout-card").classList.remove("d-none");
@ -1619,63 +1676,6 @@ function updateInventory() {
} }
document.getElementById("changeSyndicate").value = data.SupportedSyndicate ?? ""; document.getElementById("changeSyndicate").value = data.SupportedSyndicate ?? "";
document.getElementById("Boosters-list").innerHTML = "";
const now = Math.floor(Date.now() / 1000);
data.Boosters.forEach(({ ItemType, ExpiryDate }) => {
if (ExpiryDate < now) {
// Booster has expired, skip it
return;
}
const tr = document.createElement("tr");
{
const td = document.createElement("td");
td.textContent = itemMap[ItemType]?.name ?? ItemType;
tr.appendChild(td);
}
{
const td = document.createElement("td");
td.classList = "text-end text-nowrap";
const timeString = formatDatetime("%Y-%m-%d %H:%M:%s", ExpiryDate * 1000);
const inlineForm = document.createElement("form");
const input = document.createElement("input");
inlineForm.style.display = "inline-block";
inlineForm.onsubmit = function (event) {
event.preventDefault();
doChangeBoosterExpiry(ItemType, input);
};
input.type = "datetime-local";
input.classList.add("form-control");
input.classList.add("form-control-sm");
input.value = timeString;
let changed = false;
input.onchange = function () {
changed = true;
};
input.onblur = function () {
if (changed) {
doChangeBoosterExpiry(ItemType, input);
}
};
inlineForm.appendChild(input);
td.appendChild(inlineForm);
const removeButton = document.createElement("a");
removeButton.title = loc("code_remove");
removeButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>`;
removeButton.href = "#";
removeButton.onclick = function (event) {
event.preventDefault();
setBooster(ItemType, 0);
};
td.appendChild(removeButton);
tr.appendChild(td);
}
document.getElementById("Boosters-list").appendChild(tr);
});
if (single.getCurrentPath().startsWith("/webui/guildView")) { if (single.getCurrentPath().startsWith("/webui/guildView")) {
const guildReq = $.get("/custom/getGuild?guildId=" + window.guildId); const guildReq = $.get("/custom/getGuild?guildId=" + window.guildId);
guildReq.done(guildData => { guildReq.done(guildData => {
@ -1986,34 +1986,12 @@ function updateInventory() {
} }
for (const elm of accountCheats) { for (const elm of accountCheats) {
if (elm.type === "checkbox") {
elm.checked = !!data[elm.id]; elm.checked = !!data[elm.id];
} else if (elm.type === "number") {
elm.value = data[elm.id] !== undefined ? data[elm.id] : elm.getAttribute("data-default") || "";
} }
});
});
} }
function addVaultDecoRecipe() {
const uniqueName = getKey(document.getElementById("acquire-type-VaultDecoRecipes"));
if (!guildId) {
return;
}
if (!uniqueName) {
$("acquire-type-VaultDecoRecipes").addClass("is-invalid").focus();
return;
}
revalidateAuthz().then(() => {
const req = $.post({
url: "/custom/addVaultDecoRecipe?" + window.authz + "&guildId=" + window.guildId,
contentType: "application/json",
data: JSON.stringify([
{
ItemType: uniqueName,
ItemCount: 1
}
])
});
req.done(() => {
updateInventory();
}); });
}); });
} }
@ -3229,6 +3207,41 @@ document.querySelectorAll("#account-cheats input[type=checkbox]").forEach(elm =>
}; };
}); });
document.querySelectorAll("#account-cheats .input-group").forEach(grp => {
const input = grp.querySelector("input");
const select = grp.querySelector("select");
const btn = grp.querySelector("button");
if (input) {
input.oninput = input.onchange = function () {
btn.classList.remove("btn-secondary");
btn.classList.add("btn-primary");
};
}
if (select) {
select.oninput = select.onchange = function () {
btn.classList.remove("btn-secondary");
btn.classList.add("btn-primary");
};
}
btn.onclick = function () {
btn.classList.remove("btn-primary");
btn.classList.add("btn-secondary");
const input = btn.closest(".input-group").querySelector('input[type="number"]');
if (!input) return;
revalidateAuthz().then(() => {
const value = input.value;
$.post({
url: "/custom/setAccountCheat?" + window.authz,
contentType: "application/json",
data: JSON.stringify({
key: input.id,
value: parseInt(value)
})
});
});
};
});
document.querySelectorAll("#guild-cheats input[type=checkbox]").forEach(elm => { document.querySelectorAll("#guild-cheats input[type=checkbox]").forEach(elm => {
elm.onchange = function () { elm.onchange = function () {
revalidateAuthz().then(() => { revalidateAuthz().then(() => {
@ -3467,9 +3480,9 @@ function doBulkQuestUpdate(operation) {
}); });
} }
function toast(text) { function toast(text, type = "primary") {
const toast = document.createElement("div"); const toast = document.createElement("div");
toast.className = "toast align-items-center text-bg-primary border-0"; toast.className = `toast align-items-center text-bg-${type} border-0`;
const div = document.createElement("div"); const div = document.createElement("div");
div.className = "d-flex"; div.className = "d-flex";
const body = document.createElement("div"); const body = document.createElement("div");
@ -3542,7 +3555,7 @@ function handleModularSelection(category) {
}); });
} }
function setBooster(ItemType, ExpiryDate, callback) { function setBooster(ItemType, ExpiryDate) {
revalidateAuthz().then(() => { revalidateAuthz().then(() => {
$.post({ $.post({
url: "/custom/setBooster?" + window.authz, url: "/custom/setBooster?" + window.authz,
@ -3555,33 +3568,27 @@ function setBooster(ItemType, ExpiryDate, callback) {
]) ])
}).done(function () { }).done(function () {
updateInventory(); updateInventory();
if (callback) callback();
}); });
}); });
} }
function doAcquireBoosters() { function doAcquireBooster() {
const uniqueName = getKey(document.getElementById("acquire-type-Boosters")); const uniqueName = getKey(document.getElementById("acquire-type-Boosters"));
if (!uniqueName) { if (!uniqueName) {
$("#acquire-type-Boosters").addClass("is-invalid").focus(); $("#acquire-type-Boosters").addClass("is-invalid").focus();
return; return;
} }
const ExpiryDate = Date.now() / 1000 + 3 * 24 * 60 * 60; // default 3 days setBooster(uniqueName, Math.floor(Date.now() / 1000 + 3 * 24 * 60 * 60));
setBooster(uniqueName, ExpiryDate, () => { document.getElementById("acquire-type-Boosters").value = "";
$("#acquire-type-Boosters").val("");
});
} }
function doChangeBoosterExpiry(ItemType, ExpiryDateInput) { function doChangeBoosterExpiry(ItemType, ExpiryDateInput) {
console.log("Changing booster expiry for", ItemType, "to", ExpiryDateInput.value); const ExpiryDate = Math.floor(new Date(ExpiryDateInput.value).getTime() / 1000);
// cast local datetime string to unix timestamp
const ExpiryDate = Math.trunc(new Date(ExpiryDateInput.value).getTime() / 1000);
if (isNaN(ExpiryDate)) { if (isNaN(ExpiryDate)) {
ExpiryDateInput.addClass("is-invalid").focus(); ExpiryDateInput.addClass("is-invalid").focus();
return false; return;
} }
setBooster(ItemType, ExpiryDate); setBooster(ItemType, ExpiryDate);
return true;
} }
function formatDatetime(fmt, date) { function formatDatetime(fmt, date) {
@ -4027,73 +4034,31 @@ function handleModularPartsChange(event) {
}); });
} }
} }
function suitInvigorationUpgradeData(suitData) {
let expiryDate = "";
if (suitData.UpgradesExpiry) {
if (suitData.UpgradesExpiry.$date) {
expiryDate = new Date(parseInt(suitData.UpgradesExpiry.$date.$numberLong));
} else if (typeof suitData.UpgradesExpiry === "number") {
expiryDate = new Date(suitData.UpgradesExpiry);
} else if (suitData.UpgradesExpiry instanceof Date) {
expiryDate = suitData.UpgradesExpiry;
}
if (expiryDate && !isNaN(expiryDate.getTime())) {
const year = expiryDate.getFullYear();
const month = String(expiryDate.getMonth() + 1).padStart(2, "0");
const day = String(expiryDate.getDate()).padStart(2, "0");
const hours = String(expiryDate.getHours()).padStart(2, "0");
const minutes = String(expiryDate.getMinutes()).padStart(2, "0");
expiryDate = `${year}-${month}-${day}T${hours}:${minutes}`;
} else {
expiryDate = "";
}
}
return {
oid: suitData.ItemId.$oid,
OffensiveUpgrade: suitData.OffensiveUpgrade || "",
DefensiveUpgrade: suitData.DefensiveUpgrade || "",
UpgradesExpiry: expiryDate
};
}
function submitSuitInvigorationUpgrade(event) { function submitInvigoration(event) {
event.preventDefault(); event.preventDefault();
const oid = new URLSearchParams(window.location.search).get("itemId"); const OffensiveUpgrade = document.getElementById("invigoration-offensive").value;
const offensiveUpgrade = document.getElementById("dv-invigoration-offensive").value; const DefensiveUpgrade = document.getElementById("invigoration-defensive").value;
const defensiveUpgrade = document.getElementById("dv-invigoration-defensive").value; const expiry = document.getElementById("invigoration-expiry").value;
const expiry = document.getElementById("dv-invigoration-expiry").value;
if (!offensiveUpgrade || !defensiveUpgrade) { if (!OffensiveUpgrade || !DefensiveUpgrade) {
alert(loc("code_requiredInvigorationUpgrade")); toast(loc("code_requiredInvigorationUpgrade"), "warning");
return; return;
} }
const data = { setInvigoration({
OffensiveUpgrade: offensiveUpgrade, OffensiveUpgrade,
DefensiveUpgrade: defensiveUpgrade DefensiveUpgrade,
}; UpgradesExpiry: expiry ? new Date(expiry).getTime() : Date.now() + 7 * 24 * 60 * 60 * 1000
});
if (expiry) {
data.UpgradesExpiry = new Date(expiry).getTime();
} }
editSuitInvigorationUpgrade(oid, data); function setInvigoration(data) {
} const oid = new URLSearchParams(window.location.search).get("itemId");
function clearSuitInvigorationUpgrades() {
editSuitInvigorationUpgrade(new URLSearchParams(window.location.search).get("itemId"), null);
}
async function editSuitInvigorationUpgrade(oid, data) {
/* data?: {
DefensiveUpgrade: string;
OffensiveUpgrade: string;
UpgradesExpiry?: number;
}*/
$.post({ $.post({
url: "/custom/editSuitInvigorationUpgrade?" + window.authz, url: "/custom/setInvigoration?" + window.authz,
contentType: "application/json", contentType: "application/json",
data: JSON.stringify({ oid, data }) data: JSON.stringify({ oid, ...data })
}).done(function () { }).done(function () {
updateInventory(); updateInventory();
}); });

View File

@ -76,7 +76,7 @@ dict = {
code_replays: `[UNTRANSLATED] Replays`, code_replays: `[UNTRANSLATED] Replays`,
code_stalker: `Stalker`, code_stalker: `Stalker`,
code_succChange: `Erfolgreich geändert.`, code_succChange: `Erfolgreich geändert.`,
code_requiredInvigorationUpgrade: `Du musst sowohl ein offensives & defensives Upgrade auswählen.`, code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`, login_description: `Melde dich mit deinem OpenWF-Account an (denselben Angaben wie im Spiel, wenn du dich mit diesem Server verbindest).`,
login_emailLabel: `E-Mail-Adresse`, login_emailLabel: `E-Mail-Adresse`,
login_passwordLabel: `Passwort`, login_passwordLabel: `Passwort`,
@ -109,7 +109,7 @@ dict = {
inventory_moaPets: `Moas`, inventory_moaPets: `Moas`,
inventory_kubrowPets: `Bestien`, inventory_kubrowPets: `Bestien`,
inventory_evolutionProgress: `Incarnon-Entwicklungsfortschritte`, inventory_evolutionProgress: `Incarnon-Entwicklungsfortschritte`,
inventory_Boosters: `Booster`, inventory_boosters: `Booster`,
inventory_flavourItems: `<abbr title="Animationssets, Glyphen, Farbpaletten usw.">Sammlerstücke</abbr>`, inventory_flavourItems: `<abbr title="Animationssets, Glyphen, Farbpaletten usw.">Sammlerstücke</abbr>`,
inventory_shipDecorations: `Schiffsdekorationen`, inventory_shipDecorations: `Schiffsdekorationen`,
inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`, inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`,
@ -147,7 +147,7 @@ dict = {
detailedView_valenceBonusLabel: `Valenz-Bonus`, detailedView_valenceBonusLabel: `Valenz-Bonus`,
detailedView_valenceBonusDescription: `Du kannst den Valenz-Bonus deiner Waffe festlegen oder entfernen.`, detailedView_valenceBonusDescription: `Du kannst den Valenz-Bonus deiner Waffe festlegen oder entfernen.`,
detailedView_modularPartsLabel: `Modulare Teile ändern`, detailedView_modularPartsLabel: `Modulare Teile ändern`,
detailedView_suitInvigorationLabel: `Warframe-Kräftigung`, detailedView_invigorationLabel: `Kräftigung`,
detailedView_loadoutLabel: `Loadouts`, detailedView_loadoutLabel: `Loadouts`,
invigorations_offensive_AbilityStrength: `+200% Fähigkeitsstärke`, invigorations_offensive_AbilityStrength: `+200% Fähigkeitsstärke`,
@ -172,9 +172,9 @@ dict = {
invigorations_utility_Jumps: `+5 Sprung-Zurücksetzungen`, invigorations_utility_Jumps: `+5 Sprung-Zurücksetzungen`,
invigorations_utility_EnergyRegen: `+2 Energieregeneration pro Sekunde`, invigorations_utility_EnergyRegen: `+2 Energieregeneration pro Sekunde`,
invigorations_offensiveLabel: `Offensives Upgrade`, detailedView_invigorationOffensiveLabel: `Offensives Upgrade`,
invigorations_defensiveLabel: `Defensives Upgrade`, detailedView_invigorationUtilityLabel: `[UNTRANSLATED] Utility Upgrade`,
invigorations_expiryLabel: `Upgrades Ablaufdatum (optional)`, detailedView_invigorationExpiryLabel: `[UNTRANSLATED] Invigoration Expiry (optional)`,
abilityOverride_label: `Fähigkeitsüberschreibung`, abilityOverride_label: `Fähigkeitsüberschreibung`,
abilityOverride_onSlot: `auf Slot`, abilityOverride_onSlot: `auf Slot`,
@ -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`,
@ -257,6 +257,12 @@ dict = {
cheats_changeButton: `Ändern`, cheats_changeButton: `Ändern`,
cheats_markAllAsRead: `Posteingang als gelesen markieren`, cheats_markAllAsRead: `Posteingang als gelesen markieren`,
cheats_finishInvasionsInOneMission: `[UNTRANSLATED] Finish Invasions in One Mission`, cheats_finishInvasionsInOneMission: `[UNTRANSLATED] Finish Invasions in One Mission`,
cheats_nemesisHenchmenKillsMultiplierGrineer: `[UNTRANSLATED] Rage Progess Multiplier (Grineer)`,
cheats_nemesisHenchmenKillsMultiplierCorpus: `[UNTRANSLATED] Rage Progess Multiplier (Corpus)`,
cheats_nemesisAntivirusGainMultiplier: `[UNTRANSLATED] Antivirus Progress Multiplier`,
cheats_nemesisHintProgressMultiplierGrineer: `[UNTRANSLATED] Hint Progress Multiplier (Grineer)`,
cheats_nemesisHintProgressMultiplierCorpus: `[UNTRANSLATED] Hint Progress Multiplier (Corpus)`,
cheats_nemesisExtraWeapon: `[UNTRANSLATED] Extra Nemesis Weapon / Token On Vanquish (0 to disable)`,
worldState: `Weltstatus`, worldState: `Weltstatus`,
worldState_creditBoost: `Event Booster: Credit`, worldState_creditBoost: `Event Booster: Credit`,
@ -278,6 +284,7 @@ dict = {
worldState_hallowedFlame: `Geweihte Flamme`, worldState_hallowedFlame: `Geweihte Flamme`,
worldState_hallowedNightmares: `Geweihte Albträume`, worldState_hallowedNightmares: `Geweihte Albträume`,
worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`, worldState_hallowedNightmaresRewards: `[UNTRANSLATED] Hallowed Nightmares Rewards`,
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
worldState_proxyRebellion: `Proxy-Rebellion`, worldState_proxyRebellion: `Proxy-Rebellion`,
worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`, worldState_proxyRebellionRewards: `[UNTRANSLATED] Proxy Rebellion Rewards`,
worldState_bellyOfTheBeast: `Das Innere der Bestie`, worldState_bellyOfTheBeast: `Das Innere der Bestie`,

View File

@ -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`,
@ -75,7 +75,7 @@ dict = {
code_replays: `Replays`, code_replays: `Replays`,
code_stalker: `Stalker`, code_stalker: `Stalker`,
code_succChange: `Successfully changed.`, code_succChange: `Successfully changed.`,
code_requiredInvigorationUpgrade: `You must select both an offensive & defensive upgrade.`, code_requiredInvigorationUpgrade: `You must select both an offensive & utility upgrade.`,
login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`, login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`,
login_emailLabel: `Email address`, login_emailLabel: `Email address`,
login_passwordLabel: `Password`, login_passwordLabel: `Password`,
@ -108,7 +108,7 @@ dict = {
inventory_moaPets: `Moas`, inventory_moaPets: `Moas`,
inventory_kubrowPets: `Beasts`, inventory_kubrowPets: `Beasts`,
inventory_evolutionProgress: `Incarnon Evolution Progress`, inventory_evolutionProgress: `Incarnon Evolution Progress`,
inventory_Boosters: `Boosters`, inventory_boosters: `Boosters`,
inventory_flavourItems: `<abbr title="Animation Sets, Glyphs, Palettes, etc.">Flavour Items</abbr>`, inventory_flavourItems: `<abbr title="Animation Sets, Glyphs, Palettes, etc.">Flavour Items</abbr>`,
inventory_shipDecorations: `Ship Decorations`, inventory_shipDecorations: `Ship Decorations`,
inventory_bulkAddSuits: `Add Missing Warframes`, inventory_bulkAddSuits: `Add Missing Warframes`,
@ -146,7 +146,7 @@ dict = {
detailedView_valenceBonusLabel: `Valence Bonus`, detailedView_valenceBonusLabel: `Valence Bonus`,
detailedView_valenceBonusDescription: `You can set or remove the Valence Bonus from your weapon.`, detailedView_valenceBonusDescription: `You can set or remove the Valence Bonus from your weapon.`,
detailedView_modularPartsLabel: `Change Modular Parts`, detailedView_modularPartsLabel: `Change Modular Parts`,
detailedView_suitInvigorationLabel: `Warframe Invigoration`, detailedView_invigorationLabel: `Invigoration`,
detailedView_loadoutLabel: `Loadouts`, detailedView_loadoutLabel: `Loadouts`,
invigorations_offensive_AbilityStrength: `+200% Ability Strength`, invigorations_offensive_AbilityStrength: `+200% Ability Strength`,
@ -171,9 +171,9 @@ dict = {
invigorations_utility_Jumps: `+5 Jump Resets`, invigorations_utility_Jumps: `+5 Jump Resets`,
invigorations_utility_EnergyRegen: `+2 Energy Regen/s`, invigorations_utility_EnergyRegen: `+2 Energy Regen/s`,
invigorations_offensiveLabel: `Offensive Upgrade`, detailedView_invigorationOffensiveLabel: `Offensive Upgrade`,
invigorations_defensiveLabel: `Defensive Upgrade`, detailedView_invigorationUtilityLabel: `Utility Upgrade`,
invigorations_expiryLabel: `Upgrades Expiry (optional)`, detailedView_invigorationExpiryLabel: `Invigoration Expiry (optional)`,
abilityOverride_label: `Ability Override`, abilityOverride_label: `Ability Override`,
abilityOverride_onSlot: `on slot`, abilityOverride_onSlot: `on slot`,
@ -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`,
@ -256,6 +256,12 @@ dict = {
cheats_changeButton: `Change`, cheats_changeButton: `Change`,
cheats_markAllAsRead: `Mark Inbox As Read`, cheats_markAllAsRead: `Mark Inbox As Read`,
cheats_finishInvasionsInOneMission: `Finish Invasions in One Mission`, cheats_finishInvasionsInOneMission: `Finish Invasions in One Mission`,
cheats_nemesisHenchmenKillsMultiplierGrineer: `Rage Progess Multiplier (Grineer)`,
cheats_nemesisHenchmenKillsMultiplierCorpus: `Rage Progess Multiplier (Corpus)`,
cheats_nemesisAntivirusGainMultiplier: `Antivirus Progress Multiplier`,
cheats_nemesisHintProgressMultiplierGrineer: `Hint Progress Multiplier (Grineer)`,
cheats_nemesisHintProgressMultiplierCorpus: `Hint Progress Multiplier (Corpus)`,
cheats_nemesisExtraWeapon: `Extra Nemesis Weapon / Token On Vanquish (0 to disable)`,
worldState: `World State`, worldState: `World State`,
worldState_creditBoost: `Credit Boost`, worldState_creditBoost: `Credit Boost`,
@ -277,6 +283,7 @@ dict = {
worldState_hallowedFlame: `Hallowed Flame`, worldState_hallowedFlame: `Hallowed Flame`,
worldState_hallowedNightmares: `Hallowed Nightmares`, worldState_hallowedNightmares: `Hallowed Nightmares`,
worldState_hallowedNightmaresRewards: `Hallowed Nightmares Rewards`, worldState_hallowedNightmaresRewards: `Hallowed Nightmares Rewards`,
worldState_naberusNights: `Nights of Naberus`,
worldState_proxyRebellion: `Proxy Rebellion`, worldState_proxyRebellion: `Proxy Rebellion`,
worldState_proxyRebellionRewards: `Proxy Rebellion Rewards`, worldState_proxyRebellionRewards: `Proxy Rebellion Rewards`,
worldState_bellyOfTheBeast: `Belly of the Beast`, worldState_bellyOfTheBeast: `Belly of the Beast`,

View File

@ -1,7 +1,7 @@
// 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: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`, general_inventoryUpdateNoteGameWs: `Nota: Puede que necesites reabrir cualquier menú en el que te encuentres para que los cambios se reflejen.`,
general_addButton: `Agregar`, general_addButton: `Agregar`,
general_setButton: `Establecer`, general_setButton: `Establecer`,
general_none: `Ninguno`, general_none: `Ninguno`,
@ -32,8 +32,8 @@ dict = {
code_renamePrompt: `Escribe tu nuevo nombre personalizado:`, code_renamePrompt: `Escribe tu nuevo nombre personalizado:`,
code_remove: `Quitar`, code_remove: `Quitar`,
code_addItemsConfirm: `¿Estás seguro de que deseas agregar |COUNT| objetos a tu cuenta?`, code_addItemsConfirm: `¿Estás seguro de que deseas agregar |COUNT| objetos a tu cuenta?`,
code_addTechProjectsConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| research to your clan?`, code_addTechProjectsConfirm: `¿Estás seguro de que quieres añadir |COUNT| proyectos de investigación a tu clan?`,
code_addDecoRecipesConfirm: `[UNTRANSLATED] Are you sure you want to add |COUNT| deco recipes to your clan?`, code_addDecoRecipesConfirm: `¿Estás seguro de que quieres añadir |COUNT| planos de decoración a tu clan?`,
code_succRankUp: `Ascenso exitoso.`, code_succRankUp: `Ascenso exitoso.`,
code_noEquipmentToRankUp: `No hay equipo para ascender.`, code_noEquipmentToRankUp: `No hay equipo para ascender.`,
code_succAdded: `Agregado exitosamente.`, code_succAdded: `Agregado exitosamente.`,
@ -45,7 +45,7 @@ dict = {
code_rank: `Rango`, code_rank: `Rango`,
code_rankUp: `Subir de rango`, code_rankUp: `Subir de rango`,
code_rankDown: `Bajar de rango`, code_rankDown: `Bajar de rango`,
code_unlockLevelCap: `[UNTRANSLATED] Unlock level cap`, code_unlockLevelCap: `Desbloquear level cap`,
code_count: `Cantidad`, code_count: `Cantidad`,
code_focusAllUnlocked: `Todas las escuelas de enfoque ya están desbloqueadas.`, code_focusAllUnlocked: `Todas las escuelas de enfoque ya están desbloqueadas.`,
code_focusUnlocked: `¡Desbloqueadas |COUNT| nuevas escuelas de enfoque! Se necesita una actualización del inventario para reflejar los cambios en el juego. Visitar la navegación debería ser la forma más sencilla de activarlo.`, code_focusUnlocked: `¡Desbloqueadas |COUNT| nuevas escuelas de enfoque! Se necesita una actualización del inventario para reflejar los cambios en el juego. Visitar la navegación debería ser la forma más sencilla de activarlo.`,
@ -65,18 +65,18 @@ dict = {
code_completed: `Completada`, code_completed: `Completada`,
code_active: `Activa`, code_active: `Activa`,
code_pigment: `Pigmento`, code_pigment: `Pigmento`,
code_controller: `[UNTRANSLATED] Controller cursor`, code_controller: `Cursor de Mando`,
code_mouseLine: `[UNTRANSLATED] Line cursor`, code_mouseLine: `Cursor de línea`,
code_mouse: `[UNTRANSLATED] Cursor`, code_mouse: `Cursor`,
code_itemColorPalette: `Paleta de colores |ITEM|`, code_itemColorPalette: `Paleta de colores |ITEM|`,
code_mature: `Listo para el combate`, code_mature: `Listo para el combate`,
code_unmature: `Regresar el envejecimiento genético`, code_unmature: `Regresar el envejecimiento genético`,
code_fund: `[UNTRANSLATED] Fund`, code_fund: `Financiar`,
code_funded: `[UNTRANSLATED] Funded`, code_funded: `Financiado`,
code_replays: `[UNTRANSLATED] Replays`, code_replays: `Repeticiones`,
code_stalker: `Stalker`, code_stalker: `Stalker`,
code_succChange: `Cambiado correctamente`, code_succChange: `Cambiado correctamente`,
code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una defensiva.`, code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una mejora de utilidad.`,
login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`, login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`,
login_emailLabel: `Dirección de correo electrónico`, login_emailLabel: `Dirección de correo electrónico`,
login_passwordLabel: `Contraseña`, login_passwordLabel: `Contraseña`,
@ -109,7 +109,7 @@ dict = {
inventory_moaPets: `Moas`, inventory_moaPets: `Moas`,
inventory_kubrowPets: `Bestias`, inventory_kubrowPets: `Bestias`,
inventory_evolutionProgress: `Progreso de evolución Incarnon`, inventory_evolutionProgress: `Progreso de evolución Incarnon`,
inventory_Boosters: `Potenciadores`, inventory_boosters: `Potenciadores`,
inventory_flavourItems: `<abbr title="Conjuntos de animaciones, glifos, paletas, etc.">Ítems estéticos</abbr>`, inventory_flavourItems: `<abbr title="Conjuntos de animaciones, glifos, paletas, etc.">Ítems estéticos</abbr>`,
inventory_shipDecorations: `Decoraciones de nave`, inventory_shipDecorations: `Decoraciones de nave`,
inventory_bulkAddSuits: `Agregar Warframes faltantes`, inventory_bulkAddSuits: `Agregar Warframes faltantes`,
@ -118,8 +118,8 @@ dict = {
inventory_bulkAddSpaceWeapons: `Agregar armas Archwing faltantes`, inventory_bulkAddSpaceWeapons: `Agregar armas Archwing faltantes`,
inventory_bulkAddSentinels: `Agregar centinelas faltantes`, inventory_bulkAddSentinels: `Agregar centinelas faltantes`,
inventory_bulkAddSentinelWeapons: `Agregar armas de centinela faltantes`, inventory_bulkAddSentinelWeapons: `Agregar armas de centinela faltantes`,
inventory_bulkAddFlavourItems: `[UNTRANSLATED] Add Missing Flavour Items`, inventory_bulkAddFlavourItems: `Añadir items estéticos faltantes`,
inventory_bulkAddShipDecorations: `[UNTRANSLATED] Add Missing Ship Decorations`, inventory_bulkAddShipDecorations: `Añadir decoraciones de Nave Faltantes`,
inventory_bulkAddEvolutionProgress: `Completar el progreso de evolución Incarnon faltante`, inventory_bulkAddEvolutionProgress: `Completar el progreso de evolución Incarnon faltante`,
inventory_bulkRankUpSuits: `Maximizar rango de todos los Warframes`, inventory_bulkRankUpSuits: `Maximizar rango de todos los Warframes`,
inventory_bulkRankUpWeapons: `Maximizar rango de todas las armas`, inventory_bulkRankUpWeapons: `Maximizar rango de todas las armas`,
@ -147,7 +147,7 @@ dict = {
detailedView_valenceBonusLabel: `Bonus de Valéncia`, detailedView_valenceBonusLabel: `Bonus de Valéncia`,
detailedView_valenceBonusDescription: `Puedes establecer o quitar el bonus de valencia de tu arma.`, detailedView_valenceBonusDescription: `Puedes establecer o quitar el bonus de valencia de tu arma.`,
detailedView_modularPartsLabel: `Cambiar partes modulares`, detailedView_modularPartsLabel: `Cambiar partes modulares`,
detailedView_suitInvigorationLabel: `Vigorización de Warframe`, detailedView_invigorationLabel: `Fortalecimiento`,
detailedView_loadoutLabel: `Equipamientos`, detailedView_loadoutLabel: `Equipamientos`,
invigorations_offensive_AbilityStrength: `+200% Fuerza de Habilidad`, invigorations_offensive_AbilityStrength: `+200% Fuerza de Habilidad`,
@ -172,9 +172,9 @@ dict = {
invigorations_utility_Jumps: `+5 Restablecimientos de Salto`, invigorations_utility_Jumps: `+5 Restablecimientos de Salto`,
invigorations_utility_EnergyRegen: `+2 Regeneración de Energía/s`, invigorations_utility_EnergyRegen: `+2 Regeneración de Energía/s`,
invigorations_offensiveLabel: `Mejora Ofensiva`, detailedView_invigorationOffensiveLabel: `Mejora Ofensiva`,
invigorations_defensiveLabel: `Mejora Defensiva`, detailedView_invigorationUtilityLabel: `Mejora de Utilidad`,
invigorations_expiryLabel: `Caducidad de Mejoras (opcional)`, detailedView_invigorationExpiryLabel: `Caducidad del Fortalecimiento (opcional)`,
abilityOverride_label: `Intercambio de Habilidad`, abilityOverride_label: `Intercambio de Habilidad`,
abilityOverride_onSlot: `en el espacio`, abilityOverride_onSlot: `en el espacio`,
@ -257,6 +257,12 @@ dict = {
cheats_changeButton: `Cambiar`, cheats_changeButton: `Cambiar`,
cheats_markAllAsRead: `Marcar bandeja de entrada como leída`, cheats_markAllAsRead: `Marcar bandeja de entrada como leída`,
cheats_finishInvasionsInOneMission: `Finaliza Invasión en una mision`, cheats_finishInvasionsInOneMission: `Finaliza Invasión en una mision`,
cheats_nemesisHenchmenKillsMultiplierGrineer: `[UNTRANSLATED] Rage Progess Multiplier (Grineer)`,
cheats_nemesisHenchmenKillsMultiplierCorpus: `[UNTRANSLATED] Rage Progess Multiplier (Corpus)`,
cheats_nemesisAntivirusGainMultiplier: `[UNTRANSLATED] Antivirus Progress Multiplier`,
cheats_nemesisHintProgressMultiplierGrineer: `[UNTRANSLATED] Hint Progress Multiplier (Grineer)`,
cheats_nemesisHintProgressMultiplierCorpus: `[UNTRANSLATED] Hint Progress Multiplier (Corpus)`,
cheats_nemesisExtraWeapon: `[UNTRANSLATED] Extra Nemesis Weapon / Token On Vanquish (0 to disable)`,
worldState: `Estado del mundo`, worldState: `Estado del mundo`,
worldState_creditBoost: `Potenciador de Créditos`, worldState_creditBoost: `Potenciador de Créditos`,
@ -278,6 +284,7 @@ dict = {
worldState_hallowedFlame: `Llama Sagrada`, worldState_hallowedFlame: `Llama Sagrada`,
worldState_hallowedNightmares: `Pesadillas Sagradas`, worldState_hallowedNightmares: `Pesadillas Sagradas`,
worldState_hallowedNightmaresRewards: `Recompensas de Pesadillas Sagradas`, worldState_hallowedNightmaresRewards: `Recompensas de Pesadillas Sagradas`,
worldState_naberusNights: `Noches de Naberus`,
worldState_proxyRebellion: `Rebelión Proxy`, worldState_proxyRebellion: `Rebelión Proxy`,
worldState_proxyRebellionRewards: `Recompensas de Rebelión Proxy`, worldState_proxyRebellionRewards: `Recompensas de Rebelión Proxy`,
worldState_bellyOfTheBeast: `Vientre de la Bestia`, worldState_bellyOfTheBeast: `Vientre de la Bestia`,
@ -401,9 +408,9 @@ dict = {
theme_dark: `Tema Oscuro`, theme_dark: `Tema Oscuro`,
theme_light: `Tema Claro`, theme_light: `Tema Claro`,
guildView_cheats: `[UNTRANSLATED] Clan Cheats`, guildView_cheats: `Trucos de Clan`,
guildView_techProjects: `Investigación`, guildView_techProjects: `Investigación`,
guildView_vaultDecoRecipes: `[UNTRANSLATED] Dojo Deco Recipes`, guildView_vaultDecoRecipes: `Planos de Decoración de Dojo`,
guildView_alliance: `Alianza`, guildView_alliance: `Alianza`,
guildView_members: `Miembros`, guildView_members: `Miembros`,
guildView_pending: `Pendiente`, guildView_pending: `Pendiente`,
@ -423,11 +430,11 @@ dict = {
guildView_rank_soldier: `Soldado`, guildView_rank_soldier: `Soldado`,
guildView_rank_utility: `Utilitario`, guildView_rank_utility: `Utilitario`,
guildView_rank_warlord: `Señor de la guerra`, guildView_rank_warlord: `Señor de la guerra`,
guildView_currency_owned: `[UNTRANSLATED] |COUNT| in Vault.`, guildView_currency_owned: `|COUNT| en la Bóveda.`,
guildView_bulkAddTechProjects: `[UNTRANSLATED] Add Missing Research`, guildView_bulkAddTechProjects: `Añadir proyectos de Investigación Faltantes`,
guildView_bulkAddVaultDecoRecipes: `[UNTRANSLATED] Add Missing Dojo Deco Recipes`, guildView_bulkAddVaultDecoRecipes: `Añadir planos de Decoración de Dojo Faltantes`,
guildView_bulkFundTechProjects: `[UNTRANSLATED] Fund All Research`, guildView_bulkFundTechProjects: `Financiar toda la Investigación`,
guildView_bulkCompleteTechProjects: `[UNTRANSLATED] Complete All Research`, guildView_bulkCompleteTechProjects: `Completar toda la Investigación`,
guildView_promote: `Promover`, guildView_promote: `Promover`,
guildView_demote: `Degradar`, guildView_demote: `Degradar`,

View File

@ -76,7 +76,7 @@ dict = {
code_replays: `[UNTRANSLATED] Replays`, code_replays: `[UNTRANSLATED] Replays`,
code_stalker: `Stalker`, code_stalker: `Stalker`,
code_succChange: `Changement effectué.`, code_succChange: `Changement effectué.`,
code_requiredInvigorationUpgrade: `Augmentation offensive et défensive requises.`, code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
login_description: `Connexion avec les informations de connexion OpenWF.`, login_description: `Connexion avec les informations de connexion OpenWF.`,
login_emailLabel: `Email`, login_emailLabel: `Email`,
login_passwordLabel: `Mot de passe`, login_passwordLabel: `Mot de passe`,
@ -109,7 +109,7 @@ dict = {
inventory_moaPets: `Moas`, inventory_moaPets: `Moas`,
inventory_kubrowPets: `Bêtes`, inventory_kubrowPets: `Bêtes`,
inventory_evolutionProgress: `Progrès de l'évolution Incarnon`, inventory_evolutionProgress: `Progrès de l'évolution Incarnon`,
inventory_Boosters: `Boosters`, inventory_boosters: `Boosters`,
inventory_flavourItems: `[UNTRANSLATED] <abbr title="Animation Sets, Glyphs, Palettes, etc.">Flavour Items</abbr>`, inventory_flavourItems: `[UNTRANSLATED] <abbr title="Animation Sets, Glyphs, Palettes, etc.">Flavour Items</abbr>`,
inventory_shipDecorations: `Décorations du vaisseau`, inventory_shipDecorations: `Décorations du vaisseau`,
inventory_bulkAddSuits: `Ajouter les Warframes manquantes`, inventory_bulkAddSuits: `Ajouter les Warframes manquantes`,
@ -147,7 +147,7 @@ dict = {
detailedView_valenceBonusLabel: `Bonus de Valence`, detailedView_valenceBonusLabel: `Bonus de Valence`,
detailedView_valenceBonusDescription: `Définir le Bonus Valence de l'arme.`, detailedView_valenceBonusDescription: `Définir le Bonus Valence de l'arme.`,
detailedView_modularPartsLabel: `Changer l'équipement modulaire`, detailedView_modularPartsLabel: `Changer l'équipement modulaire`,
detailedView_suitInvigorationLabel: `Invigoration de Warframe`, detailedView_invigorationLabel: `Dynamisation`,
detailedView_loadoutLabel: `Équipements`, detailedView_loadoutLabel: `Équipements`,
invigorations_offensive_AbilityStrength: `+200% de puissance de pouvoir`, invigorations_offensive_AbilityStrength: `+200% de puissance de pouvoir`,
@ -172,9 +172,9 @@ dict = {
invigorations_utility_Jumps: `+5 réinitialisations de saut`, invigorations_utility_Jumps: `+5 réinitialisations de saut`,
invigorations_utility_EnergyRegen: `+2 d'énergie régénérés/s`, invigorations_utility_EnergyRegen: `+2 d'énergie régénérés/s`,
invigorations_offensiveLabel: `Amélioration offensive`, detailedView_invigorationOffensiveLabel: `Amélioration offensive`,
invigorations_defensiveLabel: `Amélioration défensive`, detailedView_invigorationUtilityLabel: `[UNTRANSLATED] Utility Upgrade`,
invigorations_expiryLabel: `Expiration de l'invigoration (optionnel)`, detailedView_invigorationExpiryLabel: `[UNTRANSLATED] Invigoration Expiry (optional)`,
abilityOverride_label: `Remplacement de pouvoir`, abilityOverride_label: `Remplacement de pouvoir`,
abilityOverride_onSlot: `Sur l'emplacement`, abilityOverride_onSlot: `Sur l'emplacement`,
@ -257,6 +257,12 @@ dict = {
cheats_changeButton: `Changer`, cheats_changeButton: `Changer`,
cheats_markAllAsRead: `Marquer la boîte de réception comme lue`, cheats_markAllAsRead: `Marquer la boîte de réception comme lue`,
cheats_finishInvasionsInOneMission: `Compléter les invasions en une mission.`, cheats_finishInvasionsInOneMission: `Compléter les invasions en une mission.`,
cheats_nemesisHenchmenKillsMultiplierGrineer: `[UNTRANSLATED] Rage Progess Multiplier (Grineer)`,
cheats_nemesisHenchmenKillsMultiplierCorpus: `[UNTRANSLATED] Rage Progess Multiplier (Corpus)`,
cheats_nemesisAntivirusGainMultiplier: `[UNTRANSLATED] Antivirus Progress Multiplier`,
cheats_nemesisHintProgressMultiplierGrineer: `[UNTRANSLATED] Hint Progress Multiplier (Grineer)`,
cheats_nemesisHintProgressMultiplierCorpus: `[UNTRANSLATED] Hint Progress Multiplier (Corpus)`,
cheats_nemesisExtraWeapon: `[UNTRANSLATED] Extra Nemesis Weapon / Token On Vanquish (0 to disable)`,
worldState: `Carte Solaire`, worldState: `Carte Solaire`,
worldState_creditBoost: `Booster de Crédit`, worldState_creditBoost: `Booster de Crédit`,
@ -278,6 +284,7 @@ dict = {
worldState_hallowedFlame: `Flamme Hantée`, worldState_hallowedFlame: `Flamme Hantée`,
worldState_hallowedNightmares: `Cauchemars Hantés`, worldState_hallowedNightmares: `Cauchemars Hantés`,
worldState_hallowedNightmaresRewards: `Récompenses Flamme Hantée Cauchemar`, worldState_hallowedNightmaresRewards: `Récompenses Flamme Hantée Cauchemar`,
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
worldState_proxyRebellion: `Rébellion Proxy`, worldState_proxyRebellion: `Rébellion Proxy`,
worldState_proxyRebellionRewards: `Récompenses Rébellion Proxy`, worldState_proxyRebellionRewards: `Récompenses Rébellion Proxy`,
worldState_bellyOfTheBeast: `Ventre de la Bête`, worldState_bellyOfTheBeast: `Ventre de la Bête`,

View File

@ -109,7 +109,7 @@ dict = {
inventory_moaPets: `МОА`, inventory_moaPets: `МОА`,
inventory_kubrowPets: `Звери`, inventory_kubrowPets: `Звери`,
inventory_evolutionProgress: `Прогресс эволюции Инкарнонов`, inventory_evolutionProgress: `Прогресс эволюции Инкарнонов`,
inventory_Boosters: `Бустеры`, inventory_boosters: `Бустеры`,
inventory_flavourItems: `<abbr title="Наборы анимаций, глифы, палитры и т. д.">Уникальные предметы</abbr>`, inventory_flavourItems: `<abbr title="Наборы анимаций, глифы, палитры и т. д.">Уникальные предметы</abbr>`,
inventory_shipDecorations: `Украшения корабля`, inventory_shipDecorations: `Украшения корабля`,
inventory_bulkAddSuits: `Добавить отсутствующие Варфреймы`, inventory_bulkAddSuits: `Добавить отсутствующие Варфреймы`,
@ -147,7 +147,7 @@ dict = {
detailedView_valenceBonusLabel: `Бонус Валентности`, detailedView_valenceBonusLabel: `Бонус Валентности`,
detailedView_valenceBonusDescription: `Вы можете установить или убрать бонус Валентности с вашего оружия.`, detailedView_valenceBonusDescription: `Вы можете установить или убрать бонус Валентности с вашего оружия.`,
detailedView_modularPartsLabel: `Изменить модульные части`, detailedView_modularPartsLabel: `Изменить модульные части`,
detailedView_suitInvigorationLabel: `Воодушевление Варфрейма`, detailedView_invigorationLabel: `Воодушевление`,
detailedView_loadoutLabel: `Конфигурации`, detailedView_loadoutLabel: `Конфигурации`,
invigorations_offensive_AbilityStrength: `+200% к силе способностей.`, invigorations_offensive_AbilityStrength: `+200% к силе способностей.`,
@ -172,9 +172,9 @@ dict = {
invigorations_utility_Jumps: `+5 сбросов прыжка.`, invigorations_utility_Jumps: `+5 сбросов прыжка.`,
invigorations_utility_EnergyRegen: `+2 к регенерации энергии в секунду.`, invigorations_utility_EnergyRegen: `+2 к регенерации энергии в секунду.`,
invigorations_offensiveLabel: `Атакующее улучшение`, detailedView_invigorationOffensiveLabel: `Атакующее улучшение`,
invigorations_defensiveLabel: `Вспомогательное улучшение`, detailedView_invigorationUtilityLabel: `Вспомогательное улучшение`,
invigorations_expiryLabel: `Срок действия Воодушевления (необязательно)`, detailedView_invigorationExpiryLabel: `Срок действия Воодушевления (необязательно)`,
abilityOverride_label: `Переопределение способности`, abilityOverride_label: `Переопределение способности`,
abilityOverride_onSlot: `в ячейке`, abilityOverride_onSlot: `в ячейке`,
@ -257,6 +257,12 @@ dict = {
cheats_changeButton: `Изменить`, cheats_changeButton: `Изменить`,
cheats_markAllAsRead: `Пометить все входящие как прочитанные`, cheats_markAllAsRead: `Пометить все входящие как прочитанные`,
cheats_finishInvasionsInOneMission: `Завершать вторжение за одну миссию`, cheats_finishInvasionsInOneMission: `Завершать вторжение за одну миссию`,
cheats_nemesisHenchmenKillsMultiplierGrineer: `[UNTRANSLATED] Rage Progess Multiplier (Grineer)`,
cheats_nemesisHenchmenKillsMultiplierCorpus: `[UNTRANSLATED] Rage Progess Multiplier (Corpus)`,
cheats_nemesisAntivirusGainMultiplier: `[UNTRANSLATED] Antivirus Progress Multiplier`,
cheats_nemesisHintProgressMultiplierGrineer: `[UNTRANSLATED] Hint Progress Multiplier (Grineer)`,
cheats_nemesisHintProgressMultiplierCorpus: `[UNTRANSLATED] Hint Progress Multiplier (Corpus)`,
cheats_nemesisExtraWeapon: `[UNTRANSLATED] Extra Nemesis Weapon / Token On Vanquish (0 to disable)`,
worldState: `Состояние мира`, worldState: `Состояние мира`,
worldState_creditBoost: `Глобальный бустер Кредитов`, worldState_creditBoost: `Глобальный бустер Кредитов`,
@ -278,6 +284,7 @@ dict = {
worldState_hallowedFlame: `Священное пламя`, worldState_hallowedFlame: `Священное пламя`,
worldState_hallowedNightmares: `Священные кошмары`, worldState_hallowedNightmares: `Священные кошмары`,
worldState_hallowedNightmaresRewards: `Награды Священных кошмаров`, worldState_hallowedNightmaresRewards: `Награды Священных кошмаров`,
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
worldState_proxyRebellion: `Восстание роботов`, worldState_proxyRebellion: `Восстание роботов`,
worldState_proxyRebellionRewards: `Награды Восстания роботов`, worldState_proxyRebellionRewards: `Награды Восстания роботов`,
worldState_bellyOfTheBeast: `Чрево зверя`, worldState_bellyOfTheBeast: `Чрево зверя`,

View File

@ -109,7 +109,7 @@ dict = {
inventory_moaPets: `МОА`, inventory_moaPets: `МОА`,
inventory_kubrowPets: `Тварини`, inventory_kubrowPets: `Тварини`,
inventory_evolutionProgress: `Прогрес еволюції Інкарнонів`, inventory_evolutionProgress: `Прогрес еволюції Інкарнонів`,
inventory_Boosters: `Посилення`, inventory_boosters: `Посилення`,
inventory_flavourItems: `<abbr title="Набори анімацій, гліфи, палітри і т. д.">Унікальні предмети</abbr>`, inventory_flavourItems: `<abbr title="Набори анімацій, гліфи, палітри і т. д.">Унікальні предмети</abbr>`,
inventory_shipDecorations: `Прикраси судна`, inventory_shipDecorations: `Прикраси судна`,
inventory_bulkAddSuits: `Додати відсутні Ворфрейми`, inventory_bulkAddSuits: `Додати відсутні Ворфрейми`,
@ -147,7 +147,7 @@ dict = {
detailedView_valenceBonusLabel: `Ознака Валентності`, detailedView_valenceBonusLabel: `Ознака Валентності`,
detailedView_valenceBonusDescription: `Ви можете встановити або прибрати ознаку Валентності з вашої зброї.`, detailedView_valenceBonusDescription: `Ви можете встановити або прибрати ознаку Валентності з вашої зброї.`,
detailedView_modularPartsLabel: `Змінити модульні частини`, detailedView_modularPartsLabel: `Змінити модульні частини`,
detailedView_suitInvigorationLabel: `Зміцнення Ворфрейма`, detailedView_invigorationLabel: `Зміцнення`,
detailedView_loadoutLabel: `Конфігурації`, detailedView_loadoutLabel: `Конфігурації`,
invigorations_offensive_AbilityStrength: `+200% до потужності здібностей.`, invigorations_offensive_AbilityStrength: `+200% до потужності здібностей.`,
@ -172,9 +172,9 @@ dict = {
invigorations_utility_Jumps: `+5 Оновлень стрибків.`, invigorations_utility_Jumps: `+5 Оновлень стрибків.`,
invigorations_utility_EnergyRegen: `+2 до відновлення енергії на секунду.`, invigorations_utility_EnergyRegen: `+2 до відновлення енергії на секунду.`,
invigorations_offensiveLabel: `Атакуюче вдосконалення`, detailedView_invigorationOffensiveLabel: `Атакуюче вдосконалення`,
invigorations_defensiveLabel: `Допоміжне вдосконалення`, detailedView_invigorationUtilityLabel: `Допоміжне вдосконалення`,
invigorations_expiryLabel: `Термін дії Зміцнення (необов'язково)`, detailedView_invigorationExpiryLabel: `Термін дії Зміцнення (необов'язково)`,
abilityOverride_label: `Перевизначення здібностей`, abilityOverride_label: `Перевизначення здібностей`,
abilityOverride_onSlot: `у комірці`, abilityOverride_onSlot: `у комірці`,
@ -257,6 +257,12 @@ dict = {
cheats_changeButton: `Змінити`, cheats_changeButton: `Змінити`,
cheats_markAllAsRead: `Помітити всі вхідні як прочитані`, cheats_markAllAsRead: `Помітити всі вхідні як прочитані`,
cheats_finishInvasionsInOneMission: `Завершувати вторгнення за одну місію`, cheats_finishInvasionsInOneMission: `Завершувати вторгнення за одну місію`,
cheats_nemesisHenchmenKillsMultiplierGrineer: `[UNTRANSLATED] Rage Progess Multiplier (Grineer)`,
cheats_nemesisHenchmenKillsMultiplierCorpus: `[UNTRANSLATED] Rage Progess Multiplier (Corpus)`,
cheats_nemesisAntivirusGainMultiplier: `[UNTRANSLATED] Antivirus Progress Multiplier`,
cheats_nemesisHintProgressMultiplierGrineer: `[UNTRANSLATED] Hint Progress Multiplier (Grineer)`,
cheats_nemesisHintProgressMultiplierCorpus: `[UNTRANSLATED] Hint Progress Multiplier (Corpus)`,
cheats_nemesisExtraWeapon: `[UNTRANSLATED] Extra Nemesis Weapon / Token On Vanquish (0 to disable)`,
worldState: `Стан світу`, worldState: `Стан світу`,
worldState_creditBoost: `Глобальне посилення Кредитів`, worldState_creditBoost: `Глобальне посилення Кредитів`,
@ -278,6 +284,7 @@ dict = {
worldState_hallowedFlame: `Священне полум'я`, worldState_hallowedFlame: `Священне полум'я`,
worldState_hallowedNightmares: `Священні жахіття`, worldState_hallowedNightmares: `Священні жахіття`,
worldState_hallowedNightmaresRewards: `Нагороди Священних жахіть`, worldState_hallowedNightmaresRewards: `Нагороди Священних жахіть`,
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
worldState_proxyRebellion: `Повстання роботів`, worldState_proxyRebellion: `Повстання роботів`,
worldState_proxyRebellionRewards: `Нагороди Повстання роботів`, worldState_proxyRebellionRewards: `Нагороди Повстання роботів`,
worldState_bellyOfTheBeast: `У лігві звіра`, worldState_bellyOfTheBeast: `У лігві звіра`,

View File

@ -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.`,
@ -76,7 +76,7 @@ dict = {
code_replays: `[UNTRANSLATED] Replays`, code_replays: `[UNTRANSLATED] Replays`,
code_stalker: `追猎者`, code_stalker: `追猎者`,
code_succChange: `更改成功`, code_succChange: `更改成功`,
code_requiredInvigorationUpgrade: `您必须同时选择一个进攻型和一个功能型活化属性.`, code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & utility upgrade.`,
login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)`, login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同)`,
login_emailLabel: `电子邮箱`, login_emailLabel: `电子邮箱`,
login_passwordLabel: `密码`, login_passwordLabel: `密码`,
@ -109,7 +109,7 @@ dict = {
inventory_moaPets: `恐鸟`, inventory_moaPets: `恐鸟`,
inventory_kubrowPets: `动物同伴`, inventory_kubrowPets: `动物同伴`,
inventory_evolutionProgress: `灵化之源进度`, inventory_evolutionProgress: `灵化之源进度`,
inventory_Boosters: `加成器`, inventory_boosters: `加成器`,
inventory_flavourItems: `<abbr title="动作表情、浮印、调色板等">装饰物品</abbr>`, inventory_flavourItems: `<abbr title="动作表情、浮印、调色板等">装饰物品</abbr>`,
inventory_shipDecorations: `飞船装饰`, inventory_shipDecorations: `飞船装饰`,
inventory_bulkAddSuits: `添加缺失战甲`, inventory_bulkAddSuits: `添加缺失战甲`,
@ -147,7 +147,7 @@ dict = {
detailedView_valenceBonusLabel: `效价加成`, detailedView_valenceBonusLabel: `效价加成`,
detailedView_valenceBonusDescription: `您可以设置或移除武器上的效价加成.`, detailedView_valenceBonusDescription: `您可以设置或移除武器上的效价加成.`,
detailedView_modularPartsLabel: `更换部件`, detailedView_modularPartsLabel: `更换部件`,
detailedView_suitInvigorationLabel: `编辑战甲活化属性`, detailedView_invigorationLabel: `活化`,
detailedView_loadoutLabel: `配置`, detailedView_loadoutLabel: `配置`,
invigorations_offensive_AbilityStrength: `+200%技能强度`, invigorations_offensive_AbilityStrength: `+200%技能强度`,
@ -172,9 +172,9 @@ dict = {
invigorations_utility_Jumps: `+5跳跃次数`, invigorations_utility_Jumps: `+5跳跃次数`,
invigorations_utility_EnergyRegen: `+2/秒能量恢复`, invigorations_utility_EnergyRegen: `+2/秒能量恢复`,
invigorations_offensiveLabel: `进攻型属性`, detailedView_invigorationOffensiveLabel: `进攻型属性`,
invigorations_defensiveLabel: `功能型属性`, detailedView_invigorationUtilityLabel: `[UNTRANSLATED] Utility Upgrade`,
invigorations_expiryLabel: `活化时效(可选)`, detailedView_invigorationExpiryLabel: `活化时效(可选)`,
abilityOverride_label: `技能替换`, abilityOverride_label: `技能替换`,
abilityOverride_onSlot: `槽位`, abilityOverride_onSlot: `槽位`,
@ -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: `资源无人机不会损毁`,
@ -257,6 +257,12 @@ dict = {
cheats_changeButton: `更改`, cheats_changeButton: `更改`,
cheats_markAllAsRead: `收件箱全部标记为已读`, cheats_markAllAsRead: `收件箱全部标记为已读`,
cheats_finishInvasionsInOneMission: `一场任务完成整场入侵`, cheats_finishInvasionsInOneMission: `一场任务完成整场入侵`,
cheats_nemesisHenchmenKillsMultiplierGrineer: `玄骸怒气倍率 (Grineer)`,
cheats_nemesisHenchmenKillsMultiplierCorpus: `玄骸怒气倍率 (Corpus)`,
cheats_nemesisAntivirusGainMultiplier: `杀毒进度倍率 (科腐者)`,
cheats_nemesisHintProgressMultiplierGrineer: `解密进度倍率 (Grineer)`,
cheats_nemesisHintProgressMultiplierCorpus: `解密进度倍率 (Corpus)`,
cheats_nemesisExtraWeapon: `额外玄骸武器/代币 (0为禁用)`,
worldState: `世界状态配置`, worldState: `世界状态配置`,
worldState_creditBoost: `现金加成`, worldState_creditBoost: `现金加成`,
@ -278,6 +284,7 @@ dict = {
worldState_hallowedFlame: `万圣之焰`, worldState_hallowedFlame: `万圣之焰`,
worldState_hallowedNightmares: `万圣噩梦`, worldState_hallowedNightmares: `万圣噩梦`,
worldState_hallowedNightmaresRewards: `万圣噩梦奖励设置`, worldState_hallowedNightmaresRewards: `万圣噩梦奖励设置`,
worldState_naberusNights: `[UNTRANSLATED] Nights of Naberus`,
worldState_proxyRebellion: `机械叛乱`, worldState_proxyRebellion: `机械叛乱`,
worldState_proxyRebellionRewards: `机械叛乱奖励设置`, worldState_proxyRebellionRewards: `机械叛乱奖励设置`,
worldState_bellyOfTheBeast: `兽之腹`, worldState_bellyOfTheBeast: `兽之腹`,