Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

26 changed files with 78 additions and 971 deletions

View File

@ -20,7 +20,6 @@ SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [confi
- `worldState.vallisOverride` can be set to `warm` or `cold` to lock the temperature on Orb Vallis.
- `worldState.duviriOverride` can be set to `joy`, `anger`, `envy`, `sorrow`, or `fear` to lock the Duviri spiral.
- `worldState.nightwaveOverride` will lock the nightwave season, assuming the client is new enough for it. Valid values:
- `RadioLegionIntermission14Syndicate` for Nora's Mix: Dreams of the Dead
- `RadioLegionIntermission13Syndicate` for Nora's Mix Vol. 9
- `RadioLegionIntermission12Syndicate` for Nora's Mix Vol. 8
- `RadioLegionIntermission11Syndicate` for Nora's Mix Vol. 7

View File

@ -41,7 +41,8 @@ function run(changedFile) {
}
const thisbuildproc = spawn(
[process.versions.bun ? "bun" : "npm", "run", cangoraw ? "verify" : "build:dev"].join(" "),
process.versions.bun ? "bun" : "npm",
["run", cangoraw ? "verify" : "build:dev"],
spawnopts
);
const thisbuildstart = Date.now();
@ -54,13 +55,8 @@ function run(changedFile) {
if (code === 0) {
console.log(`${cangoraw ? "Verified" : "Built"} in ${Date.now() - thisbuildstart} ms`);
runproc = spawn(
[
process.versions.bun ? "bun" : "npm",
"run",
cangoraw ? (process.versions.bun ? "raw:bun" : "raw") : "start",
"--",
...args
].join(" "),
process.versions.bun ? "bun" : "npm",
["run", cangoraw ? (process.versions.bun ? "raw:bun" : "raw") : "start", "--", ...args],
spawnopts
);
runproc.on("exit", () => {

View File

@ -17,7 +17,7 @@ const app = express();
app.use((req, _res, next) => {
// 38.5.0 introduced "ezip" for encrypted body blobs and "e" for request verification only (encrypted body blobs with no application data).
// The client patch is expected to decrypt it for us but having an unsupported Content-Encoding here would still be an issue for Express, so removing it.
// The bootstrapper decrypts it for us but having an unsupported Content-Encoding here would still be an issue for Express, so removing it.
if (req.headers["content-encoding"] == "ezip" || req.headers["content-encoding"] == "e") {
req.headers["content-encoding"] = undefined;
}

View File

@ -1,5 +1,3 @@
export const EPOCH = 1734307200_000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be the start of winter in 1999 iteration 0
const millisecondsPerSecond = 1000;
const secondsPerMinute = 60;
const minutesPerHour = 60;

View File

@ -1,8 +1,8 @@
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getSeasonChallengePools, getWorldState, pushWeeklyActs } from "../../services/worldStateService.ts";
import { EPOCH, unixTimesInMs } from "../../constants/timeConstants.ts";
import { EPOCH, getSeasonChallengePools, getWorldState, pushWeeklyActs } from "../../services/worldStateService.ts";
import { unixTimesInMs } from "../../constants/timeConstants.ts";
import type { ISeasonChallenge } from "../../types/worldStateTypes.ts";
import { ExportChallenges } from "warframe-public-export-plus";

View File

@ -1,12 +1,12 @@
import type { RequestHandler } from "express";
import { parseString } from "../../helpers/general.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { giveKeyChainItem } from "../../services/questService.ts";
import type { IKeyChainRequest } from "../../types/requestTypes.ts";
import { getAccountIdForRequest } from "../../services/loginService.ts";
export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const accountId = parseString(req.query.accountId);
const keyChainInfo = getJSONfromString<IKeyChainRequest>((req.body as string).toString());
const inventory = await getInventory(accountId);

View File

@ -1,17 +0,0 @@
import type { RequestHandler } from "express";
import { getAccountIdForRequest } from "../../services/loginService.ts";
import { resetQuestKeyToStage } from "../../services/questService.ts";
import { getInventory } from "../../services/inventoryService.ts";
import { getJSONfromString } from "../../helpers/stringHelpers.ts";
import type { IKeyChainRequest } from "../../types/requestTypes.ts";
export const reverseQuestProgressController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const keyChainInfo = getJSONfromString<IKeyChainRequest>((req.body as string).toString());
const inventory = await getInventory(accountId);
resetQuestKeyToStage(inventory, keyChainInfo);
await inventory.save();
res.end();
};

View File

@ -36,11 +36,6 @@ export const completeAllMissionsController: RequestHandler = async (req, res) =>
}
addString(inventory.NodeIntrosCompleted, "TeshinHardModeUnlocked");
addString(inventory.NodeIntrosCompleted, "CetusSyndicate_IntroJob");
let syndicate = inventory.Affiliations.find(x => x.Tag == "CetusSyndicate");
if (!syndicate) {
syndicate =
inventory.Affiliations[inventory.Affiliations.push({ Tag: "CetusSyndicate", Standing: 250, Title: 0 })]; // Non-zero standing avoids Konzu's "prove yourself" text. 250 is identical to newbie bounty + bonus
}
await inventory.save();
res.end();
};

View File

@ -61,7 +61,6 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
break;
}
inventory.QuestKeys.pull({ ItemType: questItemType });
if (inventory.ActiveQuest == questItemType) inventory.ActiveQuest = "";
break;
}
case "completeKey": {

View File

@ -120,7 +120,6 @@ import { rerollRandomModController } from "../controllers/api/rerollRandomModCon
import { researchMushroomController } from "../controllers/api/researchMushroomController.ts";
import { resetQuestProgressController } from "../controllers/api/resetQuestProgressController.ts";
import { retrievePetFromStasisController } from "../controllers/api/retrievePetFromStasisController.ts";
import { reverseQuestProgressController } from "../controllers/api/reverseQuestProgressController.ts";
import { saveDialogueController } from "../controllers/api/saveDialogueController.ts";
import { saveLoadoutController } from "../controllers/api/saveLoadoutController.ts";
import { saveSettingsController } from "../controllers/api/saveSettingsController.ts";
@ -324,7 +323,6 @@ apiRouter.post("/renamePet.php", renamePetController);
apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
apiRouter.post("/researchMushroom.php", researchMushroomController);
apiRouter.post("/retrievePetFromStasis.php", retrievePetFromStasisController);
apiRouter.post("/reverseQuestProgress.php", reverseQuestProgressController);
apiRouter.post("/saveDialogue.php", saveDialogueController);
apiRouter.post("/saveLoadout.php", saveLoadoutController);
apiRouter.post("/saveSettings.php", saveSettingsController);

View File

@ -1,425 +0,0 @@
import type { TFaction, TMissionType } from "warframe-public-export-plus";
import type { CalendarSeasonType, IConquest, IConquestMission, TConquestType } from "../types/worldStateTypes.ts";
import { mixSeeds, SRng } from "./rngService.ts";
import { EPOCH } from "../constants/timeConstants.ts";
const missionAndFactionTypes: Record<TConquestType, Partial<Record<TMissionType, TFaction[]>>> = {
CT_LAB: {
MT_EXTERMINATION: ["FC_MITW"],
MT_SURVIVAL: ["FC_MITW"],
MT_ALCHEMY: ["FC_MITW"],
MT_DEFENSE: ["FC_MITW"],
MT_ARTIFACT: ["FC_MITW"]
},
CT_HEX: {
MT_EXTERMINATION: ["FC_SCALDRA", "FC_TECHROT"],
MT_SURVIVAL: ["FC_SCALDRA", "FC_TECHROT"],
MT_DEFENSE: ["FC_SCALDRA"],
MT_ENDLESS_CAPTURE: ["FC_TECHROT"]
}
};
const assassinationFactionOptions: Record<TConquestType, TFaction[]> = {
CT_LAB: ["FC_MITW"],
CT_HEX: ["FC_SCALDRA"]
};
type TConquestDifficulty = "CD_NORMAL" | "CD_HARD";
interface IConquestConditional {
tag: string;
missionType?: TMissionType;
conquest?: TConquestType;
difficulty?: TConquestDifficulty;
season?: CalendarSeasonType;
}
const deviations: readonly IConquestConditional[] = [
{
tag: "AlchemicalShields",
missionType: "MT_ALCHEMY"
},
{
tag: "ContaminationZone",
missionType: "MT_SURVIVAL",
conquest: "CT_HEX"
},
{
tag: "DoubleTrouble",
missionType: "MT_ARTIFACT"
},
{
tag: "EscalateImmediately",
missionType: "MT_EXTERMINATION",
conquest: "CT_HEX"
},
{
tag: "EximusGrenadiers",
missionType: "MT_ALCHEMY"
},
{
tag: "FortifiedFoes",
missionType: "MT_EXTERMINATION"
},
{
tag: "FragileNodes",
missionType: "MT_ARTIFACT"
},
{
tag: "GrowingIncursion",
missionType: "MT_EXTERMINATION",
conquest: "CT_LAB"
},
{
tag: "HarshWords",
missionType: "MT_DEFENSE",
conquest: "CT_LAB"
},
{
tag: "HighScalingLegacyte",
missionType: "MT_ENDLESS_CAPTURE",
conquest: "CT_HEX"
},
{
tag: "DoubleTroubleLegacyte",
missionType: "MT_ENDLESS_CAPTURE",
conquest: "CT_HEX"
},
{
tag: "HostileSecurity",
missionType: "MT_DEFENSE",
conquest: "CT_LAB"
},
{
tag: "InfiniteTide",
missionType: "MT_ASSASSINATION",
conquest: "CT_LAB"
},
{
tag: "LostInTranslation",
missionType: "MT_DEFENSE",
conquest: "CT_LAB"
},
{
tag: "MutatedEnemies",
missionType: "MT_ENDLESS_CAPTURE",
conquest: "CT_HEX"
},
{
tag: "NecramechActivation",
missionType: "MT_SURVIVAL",
conquest: "CT_LAB"
},
{
tag: "Reinforcements",
missionType: "MT_ASSASSINATION",
conquest: "CT_LAB"
},
{
tag: "StickyFingers",
missionType: "MT_ARTIFACT",
conquest: "CT_LAB"
},
{
tag: "TankStrongArmor",
missionType: "MT_ASSASSINATION",
conquest: "CT_HEX"
},
{
tag: "TankReinforcements",
missionType: "MT_ASSASSINATION",
conquest: "CT_HEX"
},
{
tag: "TankSuperToxic",
missionType: "MT_ASSASSINATION",
conquest: "CT_HEX"
},
{
tag: "TechrotConjunction",
missionType: "MT_SURVIVAL",
conquest: "CT_HEX"
},
{
tag: "UnpoweredCapsules",
missionType: "MT_SURVIVAL",
conquest: "CT_LAB"
},
{
tag: "VolatileGrenades",
missionType: "MT_ALCHEMY"
},
{
tag: "GestatingTumors",
missionType: "MT_SURVIVAL",
conquest: "CT_HEX"
},
{
tag: "ChemicalNoise",
missionType: "MT_DEFENSE",
conquest: "CT_HEX"
},
{
tag: "ExplosiveEnergy",
missionType: "MT_DEFENSE",
conquest: "CT_HEX"
},
{
tag: "DisruptiveSounds",
missionType: "MT_DEFENSE",
conquest: "CT_HEX"
}
];
const risks: readonly IConquestConditional[] = [
{
tag: "Voidburst"
},
{
tag: "RegeneratingEnemies"
},
{
tag: "VoidAberration"
},
{
tag: "ShieldedFoes"
},
{
tag: "PointBlank"
},
{
tag: "Deflectors",
conquest: "CT_LAB"
},
{
tag: "AcceleratedEnemies"
},
{
tag: "DrainingResiduals"
},
{
tag: "Quicksand"
},
{
tag: "AntiMaterialWeapons",
conquest: "CT_LAB"
},
{
tag: "ExplosiveCrawlers",
conquest: "CT_LAB"
},
{
tag: "EMPBlackHole",
conquest: "CT_LAB"
},
{
tag: "ArtilleryBeacons",
conquest: "CT_HEX"
},
{
tag: "InfectedTechrot",
conquest: "CT_HEX"
},
{
tag: "BalloonFest",
conquest: "CT_HEX"
},
{
tag: "MiasmiteHive",
conquest: "CT_HEX"
},
{
tag: "CompetitionSpillover",
conquest: "CT_HEX"
},
{
tag: "HostileOvergrowth",
conquest: "CT_HEX"
},
{
tag: "MurmurIncursion",
conquest: "CT_HEX"
},
{
tag: "FactionSwarm_Techrot",
conquest: "CT_HEX",
difficulty: "CD_NORMAL"
},
{
tag: "FactionSwarm_Scaldra",
conquest: "CT_HEX",
difficulty: "CD_NORMAL"
},
{
tag: "HeavyWarfare",
conquest: "CT_HEX"
},
{
tag: "ArcadeAutomata",
conquest: "CT_HEX",
difficulty: "CD_NORMAL"
},
{
tag: "EfervonFog",
conquest: "CT_HEX"
},
{
tag: "WinterFrost",
conquest: "CT_HEX",
season: "CST_WINTER"
},
{
tag: "JadeSpring",
conquest: "CT_HEX",
season: "CST_SPRING"
},
{
tag: "ExplosiveSummer",
conquest: "CT_HEX",
season: "CST_SUMMER"
},
{
tag: "FallFog",
conquest: "CT_HEX",
season: "CST_FALL"
}
];
const filterConditionals = (
arr: readonly IConquestConditional[],
missionType: TMissionType | null,
conquest: TConquestType | null,
difficulty: TConquestDifficulty | null,
season: CalendarSeasonType | null
): string[] => {
const applicable = [];
for (const cond of arr) {
if (
(!cond.missionType || cond.missionType == missionType) &&
(!cond.conquest || cond.conquest == conquest) &&
(!cond.difficulty || cond.difficulty == difficulty) &&
(!cond.season || cond.season == season)
) {
applicable.push(cond.tag);
}
}
return applicable;
};
const buildMission = (
rng: SRng,
conquest: TConquestType,
missionType: TMissionType,
faction: TFaction,
season: CalendarSeasonType | null
): IConquestMission => {
const deviation = rng.randomElement(filterConditionals(deviations, missionType, conquest, null, season))!;
const easyRisk = rng.randomElement(filterConditionals(risks, missionType, conquest, "CD_NORMAL", season))!;
const hardRiskOptions = filterConditionals(risks, missionType, conquest, "CD_HARD", season);
{
const i = hardRiskOptions.indexOf(easyRisk);
if (i != -1) {
hardRiskOptions.splice(i, 1);
}
}
const hardRisk = rng.randomElement(hardRiskOptions)!;
return {
faction,
missionType,
difficulties: [
{
type: "CD_NORMAL",
deviation,
risks: [easyRisk]
},
{
type: "CD_HARD",
deviation,
risks: [easyRisk, hardRisk]
}
]
};
};
const conquestStartingDay: Record<TConquestType, number> = {
CT_LAB: 3703,
CT_HEX: 4053
};
// This function produces identical results to clients pre-40.0.0.
const getFrameVariables = (conquestType: TConquestType, time: number): [string, string, string, string] => {
const day = Math.floor((time - 1391990400_000) / 86400_000) - conquestStartingDay[conquestType];
const week = Math.floor(day / 7) + 1;
const frameVariables = [
"Framecurse",
"Knifestep",
"Exhaustion",
"Gearless",
"TimeDilation",
"Armorless",
"Starvation",
"ShieldDelay",
"Withering",
"ContactDamage",
"AbilityLockout",
"OperatorLockout",
"EnergyStarved",
"OverSensitive",
"AntiGuard",
"DecayingFlesh",
"VoidEnergyOverload",
"DullBlades",
"Undersupplied"
];
const mag = Math.floor(frameVariables.length / 4);
const rng = new SRng(conquestStartingDay[conquestType] + Math.floor(week / mag));
rng.shuffleArray(frameVariables);
const i = week % mag;
return [frameVariables[i], frameVariables[i + 1], frameVariables[i + 2], frameVariables[i + 3]];
};
export const getConquest = (
conquestType: TConquestType,
week: number,
season: CalendarSeasonType | null
): IConquest => {
const rng = new SRng(mixSeeds(conquestStartingDay[conquestType], week));
const missions: IConquestMission[] = [];
{
const missionOptions = Object.entries(missionAndFactionTypes[conquestType]);
{
const i = rng.randomInt(0, missionOptions.length - 1);
const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
missions.push(
buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
);
}
{
const i = rng.randomInt(0, missionOptions.length - 1);
const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
missions.push(
buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
);
}
missionOptions.push(["MT_ASSASSINATION", assassinationFactionOptions[conquestType]]);
{
const i = rng.randomInt(0, missionOptions.length - 1);
const [missionType, factionOptions] = missionOptions.splice(i, 1)[0];
missions.push(
buildMission(rng, conquestType, missionType as TMissionType, rng.randomElement(factionOptions)!, season)
);
}
}
const weekStart = EPOCH + week * 604800000;
const weekEnd = weekStart + 604800000;
return {
Activation: { $date: { $numberLong: weekStart.toString() } },
Expiry: { $date: { $numberLong: weekEnd.toString() } },
Type: conquestType,
Missions: missions,
Variables: getFrameVariables(conquestType, weekStart),
RandomSeed: rng.randomInt(0, 1_000_000)
};
};

View File

@ -41,8 +41,8 @@ export const createAccount = async (accountData: IDatabaseAccountRequiredFields)
await account.save();
const loadoutId = await createLoadout(account._id);
const shipId = await createShip(account._id);
await createPersonalRooms(account._id, shipId);
await createInventory(account._id, { loadOutPresetId: loadoutId, ship: shipId });
await createPersonalRooms(account._id, shipId);
await createStats(account._id.toString());
return account.toJSON();
} catch (error) {
@ -64,6 +64,17 @@ export const createPersonalRooms = async (accountId: Types.ObjectId, shipId: Typ
personalRoomsOwnerId: accountId,
activeShipId: shipId
});
if (config.skipTutorial) {
// unlocked during Vor's Prize and The Teacher quests
const defaultFeatures = [
"/Lotus/Types/Items/ShipFeatureItems/MercuryNavigationFeatureItem",
"/Lotus/Types/Items/ShipFeatureItems/ArsenalFeatureItem",
"/Lotus/Types/Items/ShipFeatureItems/SocialMenuFeatureItem",
"/Lotus/Types/Items/ShipFeatureItems/FoundryFeatureItem",
"/Lotus/Types/Items/ShipFeatureItems/ModsFeatureItem"
];
personalRooms.Ship.Features.push(...defaultFeatures);
}
await personalRooms.save();
};

View File

@ -2031,7 +2031,7 @@ function getRandomMissionDrops(
.replace("SteelPath", "Steel")
.replace(
"/Lotus/Types/Gameplay/NokkoColony/Jobs/NokkoJob",
"/Lotus/Types/Game/MissionDecks/NokkoColonyRewards/NokkoColonyRewards"
"Lotus/Types/Game/MissionDecks/NokkoColonyRewards/NokkoColonyRewards"
),
masteryReq: 0,
minEnemyLevel: 30,

View File

@ -2,25 +2,16 @@ import type { IKeyChainRequest } from "../types/requestTypes.ts";
import { isEmptyObject } from "../helpers/general.ts";
import type { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel.ts";
import { createMessage } from "./inboxService.ts";
import {
addEquipment,
addItem,
addItems,
addKeyChainItems,
addPowerSuit,
setupKahlSyndicate
} from "./inventoryService.ts";
import { addItem, addItems, addKeyChainItems, setupKahlSyndicate } from "./inventoryService.ts";
import { fromStoreItem, getKeyChainMessage, getLevelKeyRewards } from "./itemDataService.ts";
import type { IQuestKeyClient, IQuestKeyDatabase, IQuestStage } from "../types/inventoryTypes/inventoryTypes.ts";
import { logger } from "../utils/logger.ts";
import { ExportKeys, ExportRecipes } from "warframe-public-export-plus";
import { ExportKeys } from "warframe-public-export-plus";
import { addFixedLevelRewards } from "./missionInventoryUpdateService.ts";
import type { IInventoryChanges } from "../types/purchaseTypes.ts";
import questCompletionItems from "../../static/fixed_responses/questCompletionRewards.json" with { type: "json" };
import type { ITypeCount } from "../types/commonTypes.ts";
import { addString } from "../helpers/stringHelpers.ts";
import { unlockShipFeature } from "./personalRoomsService.ts";
import { EquipmentFeatures } from "../types/equipmentTypes.ts";
export interface IUpdateQuestRequest {
QuestKeys: IQuestKeyClient[];
@ -101,26 +92,6 @@ export const updateQuestStage = (
}
};
export const resetQuestKeyToStage = (
inventory: TInventoryDatabaseDocument,
{ KeyChain, ChainStage }: IKeyChainRequest
): void => {
const quest = inventory.QuestKeys.find(quest => quest.ItemType === KeyChain);
if (!quest) {
throw new Error(`Quest ${KeyChain} not found in QuestKeys`);
}
quest.Progress ??= [];
const run = quest.Progress[0]?.c ?? 0;
if (run >= 0) {
for (let i = ChainStage; i < quest.Progress.length; ++i) {
quest.Progress[i].c = run - 1;
}
}
};
export const addQuestKey = (
inventory: TInventoryDatabaseDocument,
questKey: IQuestKeyDatabase
@ -198,7 +169,6 @@ export const completeQuest = async (
stage.c = run;
await giveKeyChainStageTriggered(inventory, { KeyChain: questKey, ChainStage: i }, sendMessages);
await giveKeyChainMissionReward(inventory, { KeyChain: questKey, ChainStage: i });
await installShipFeatures(inventory, { KeyChain: questKey, ChainStage: i });
}
}
@ -335,9 +305,6 @@ const handleQuestCompletion = async (
logger.debug(`quest completion items`, questCompletionItems);
if (questCompletionItems) {
await addItems(inventory, questCompletionItems, inventoryChanges);
for (const item of questCompletionItems) {
await removeRequiredItems(inventory, item.ItemType);
}
}
};
@ -387,12 +354,7 @@ export const giveKeyChainMessage = async (
await createMessage(inventory.accountOwnerId, [keyChainMessage]);
} else {
if (keyChainMessage.countedAtt?.length) await addItems(inventory, keyChainMessage.countedAtt);
if (keyChainMessage.att?.length) {
await addItems(inventory, keyChainMessage.att);
for (const reward of keyChainMessage.att) {
await removeRequiredItems(inventory, reward);
}
}
if (keyChainMessage.att?.length) await addItems(inventory, keyChainMessage.att);
}
updateQuestStage(inventory, keyChainInfo, { m: true });
@ -417,7 +379,6 @@ export const giveKeyChainMissionReward = async (
for (const reward of missionRewards) {
await addItem(inventory, fromStoreItem(reward.StoreItem), reward.ItemCount);
await removeRequiredItems(inventory, fromStoreItem(reward.StoreItem));
}
updateQuestStage(inventory, keyChainInfo, { c: run });
@ -431,7 +392,6 @@ export const giveKeyChainMissionReward = async (
await addItem(inventory, fromStoreItem(reward.itemType), reward.amount);
} else {
await addItem(inventory, fromStoreItem(reward.itemType));
await removeRequiredItems(inventory, fromStoreItem(reward.itemType));
}
}
@ -460,337 +420,3 @@ export const giveKeyChainStageTriggered = async (
}
}
};
export const installShipFeatures = async (
inventory: TInventoryDatabaseDocument,
keyChainInfo: IKeyChainRequest
): Promise<void> => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const chainStages = ExportKeys[keyChainInfo.KeyChain]?.chainStages;
const questKey = inventory.QuestKeys.find(qk => qk.ItemType === keyChainInfo.KeyChain);
if (chainStages && questKey) {
if (keyChainInfo.ChainStage - 1 >= 0) {
const prevStage = chainStages[keyChainInfo.ChainStage - 1];
for (const item of prevStage.itemsToGiveWhenTriggered) {
if (item.startsWith("/Lotus/StoreItems/Types/Items/ShipFeatureItems/")) {
logger.debug(`installing ship feature ${fromStoreItem(item)}`);
await unlockShipFeature(inventory, fromStoreItem(item));
}
}
if (prevStage.key) {
const fixedLevelRewards = getLevelKeyRewards(prevStage.key);
if (fixedLevelRewards.levelKeyRewards?.items) {
for (const item of fixedLevelRewards.levelKeyRewards.items) {
if (item.startsWith("/Lotus/StoreItems/Types/Items/ShipFeatureItems/")) {
logger.debug(`installing ship feature ${fromStoreItem(item)}`);
await unlockShipFeature(inventory, fromStoreItem(item));
}
}
}
if (fixedLevelRewards.levelKeyRewards2) {
for (const item of fixedLevelRewards.levelKeyRewards2) {
if (
item.rewardType == "RT_STORE_ITEM" &&
item.itemType.startsWith("/Lotus/StoreItems/Types/Items/ShipFeatureItems/")
) {
logger.debug(`installing ship feature ${fromStoreItem(item.itemType)}`);
await unlockShipFeature(inventory, fromStoreItem(item.itemType));
}
}
}
}
}
}
};
export const removeRequiredItems = async (inventory: TInventoryDatabaseDocument, typeName: string): Promise<void> => {
switch (typeName) {
case "/Lotus/Types/Recipes/WarframeRecipes/MagicianHelmetBlueprint": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Keys/LimboQuest/LimboHelmetKeyBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
}
}
break;
}
case "/Lotus/Types/Recipes/WarframeRecipes/MagicianSystemsBlueprint": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Keys/LimboQuest/LimboSystemsKeyBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
}
}
break;
}
case "/Lotus/Types/Recipes/WarframeRecipes/MagicianChassisBlueprint": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Keys/LimboQuest/LimboChassisKeyBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
}
}
break;
}
case "/Lotus/Types/Recipes/WarframeRecipes/BrawlerBlueprint": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Recipes/Components/InfestedIrradiatedBaitBallBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
await addItem(inventory, recipe.resultType, -1, false, undefined, undefined, true);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
}
}
break;
}
case "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Recipes/ArchwingRecipes/StandardArchwing/StandardArchwingBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, recipeItem.ItemCount * -1);
await addItems(inventory, [
{
ItemType:
"/Lotus/Types/Recipes/ArchwingRecipes/StandardArchwing/StandardArchwingWingsBlueprint",
ItemCount: -1
},
{
ItemType:
"/Lotus/Types/Recipes/ArchwingRecipes/StandardArchwing/StandardArchwingChassisBlueprint",
ItemCount: -1
},
{
ItemType:
"/Lotus/Types/Recipes/ArchwingRecipes/StandardArchwing/StandardArchwingSystemsBlueprint",
ItemCount: -1
}
]);
}
}
break;
}
case "/Lotus/Types/Keys/ModQuest/ModQuestKeyChain": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Recipes/Components/VorBoltRemoverBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
}
}
break;
}
case "/Lotus/Types/Recipes/WarframeRecipes/ChromaBlueprint": {
await addItems(inventory, [
{
ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconABlueprint",
ItemCount: -1
},
{
ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconBBlueprint",
ItemCount: -1
},
{
ItemType: "/Lotus/Types/Recipes/WarframeRecipes/ChromaBeaconCBlueprint",
ItemCount: -1
}
]);
break;
}
case "/Lotus/Types/Recipes/WarframeRecipes/OctaviaBlueprint": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Keys/BardQuest/BardQuestSequencerBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, -1);
await addItems(inventory, [
{
ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartA",
ItemCount: -1
},
{
ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartB",
ItemCount: -1
},
{
ItemType: "/Lotus/Types/Keys/BardQuest/BardQuestSequencerPartC",
ItemCount: -1
}
]);
}
}
break;
}
case "/Lotus/Types/Game/CrewShip/Ships/RailJack": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Recipes/Railjack/RailjackCephalonBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, recipeItem.ItemCount * -1);
await unlockShipFeature(inventory, recipe.resultType);
}
}
break;
}
case "/Lotus/Types/Items/ShipDecos/MummyQuestVessel": {
const gearItem = inventory.Consumables.find(
i => i.ItemType == "/Lotus/Types/Keys/MummyQuest/MummyArtifact01GearItem"
);
if (gearItem && gearItem.ItemCount > 0) {
await addItem(inventory, gearItem.ItemType, gearItem.ItemCount * -1);
}
break;
}
case "/Lotus/Types/Recipes/WarframeRecipes/ConcreteFrameBlueprint": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Gameplay/EntratiLab/Quest/GargoyleRecipeItem"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
await addItem(inventory, recipe.resultType);
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, recipeItem.ItemCount * -1);
}
}
break;
}
case "/Lotus/Upgrades/Skins/Umbra/UmbraAltHelmet": {
const recipeItem = inventory.Recipes.find(
i => i.ItemType == "/Lotus/Types/Recipes/WarframeRecipes/ExcaliburUmbraBlueprint"
);
if (recipeItem && recipeItem.ItemCount > 0) {
const recipe = ExportRecipes[recipeItem.ItemType];
if (!inventory.MiscItems.find(i => i.ItemType == recipe.resultType)) {
const umbraModA = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModA",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const umbraModB = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModB",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const umbraModC = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Umbra/WarframeUmbraModC",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const sacrificeModA = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModA",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
const sacrificeModB = (
await addItem(
inventory,
"/Lotus/Upgrades/Mods/Sets/Sacrifice/MeleeSacrificeModB",
1,
false,
undefined,
`{"lvl":5}`
)
).Upgrades![0];
await addPowerSuit(inventory, "/Lotus/Powersuits/Excalibur/ExcaliburUmbra", {
Configs: [
{
Upgrades: [
"",
"",
"",
"",
"",
umbraModA.ItemId.$oid,
umbraModB.ItemId.$oid,
umbraModC.ItemId.$oid
]
}
],
XP: 900_000,
Features: EquipmentFeatures.DOUBLE_CAPACITY
});
inventory.XPInfo.push({
ItemType: "/Lotus/Powersuits/Excalibur/ExcaliburUmbra",
XP: 900_000
});
addEquipment(inventory, "Melee", "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana", {
Configs: [
{
Upgrades: ["", "", "", "", "", "", sacrificeModA.ItemId.$oid, sacrificeModB.ItemId.$oid]
}
],
XP: 450_000,
Features: EquipmentFeatures.DOUBLE_CAPACITY
});
inventory.XPInfo.push({
ItemType: "/Lotus/Weapons/Tenno/Melee/Swords/UmbraKatana/UmbraKatana",
XP: 450_000
});
if (recipe.consumeOnUse) await addItem(inventory, recipeItem.ItemType, recipeItem.ItemCount * -1);
}
}
break;
}
default:
break;
}
};

View File

@ -10,7 +10,7 @@ import invasionNodes from "../../static/fixed_responses/worldState/invasionNodes
import invasionRewards from "../../static/fixed_responses/worldState/invasionRewards.json" with { type: "json" };
import pvpChallenges from "../../static/fixed_responses/worldState/pvpChallenges.json" with { type: "json" };
import { buildConfig } from "./buildConfigService.ts";
import { EPOCH, unixTimesInMs } from "../constants/timeConstants.ts";
import { unixTimesInMs } from "../constants/timeConstants.ts";
import { config } from "./configService.ts";
import { getRandomElement, getRandomInt, sequentiallyUniqueRandomElement, SRng } from "./rngService.ts";
import type { IMissionReward, IRegion, TFaction } from "warframe-public-export-plus";
@ -41,7 +41,6 @@ import type {
import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers.ts";
import { logger } from "../utils/logger.ts";
import { DailyDeal, Fissure } from "../models/worldStateModel.ts";
import { getConquest } from "./conquestService.ts";
const sortieBosses = [
"SORTIE_BOSS_HYENA",
@ -277,6 +276,8 @@ const microplanetEndlessJobs: readonly string[] = [
"/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessPurifyBounty"
];
export const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
const isBeforeNextExpectedWorldStateRefresh = (nowMs: number, thenMs: number): boolean => {
return nowMs + 300_000 > thenMs;
};
@ -3181,7 +3182,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
// Nightwave Challenges
const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
if (nightwaveSyndicateTag) {
const nightwaveStartTimestamp = nightwaveTagToActivation[nightwaveSyndicateTag] ?? 1747851300000;
const nightwaveStartTimestamp = 1747851300000;
const nightwaveSeason = nightwaveTagToSeason[nightwaveSyndicateTag];
worldState.SeasonInfo = {
Activation: { $date: { $numberLong: nightwaveStartTimestamp.toString() } },
@ -3468,18 +3469,6 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
}
}
// Void Storms
const hour = Math.trunc(timeMs / unixTimesInMs.hour);
const overLastHourStormExpiry = hour * unixTimesInMs.hour + 10 * unixTimesInMs.minute;
const thisHourStormActivation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute;
if (overLastHourStormExpiry > timeMs) {
pushVoidStorms(worldState.VoidStorms, hour - 2);
}
pushVoidStorms(worldState.VoidStorms, hour - 1);
if (isBeforeNextExpectedWorldStateRefresh(timeMs, thisHourStormActivation)) {
pushVoidStorms(worldState.VoidStorms, hour);
}
// Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
{
const rollover = getSortieTime(day);
@ -3562,18 +3551,16 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
worldState.KnownCalendarSeasons.push(getCalendarSeason(week + 1));
}
if (!buildLabel || version_compare(buildLabel, "2025.10.14.16.10") >= 0) {
worldState.Conquests = [];
{
const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[week % 4];
worldState.Conquests.push(getConquest("CT_LAB", week, null));
worldState.Conquests.push(getConquest("CT_HEX", week, season));
}
if (isBeforeNextExpectedWorldStateRefresh(timeMs, weekEnd)) {
const season = (["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"] as const)[(week + 1) % 4];
worldState.Conquests.push(getConquest("CT_LAB", week, null));
worldState.Conquests.push(getConquest("CT_HEX", week, season));
}
// Void Storms
const hour = Math.trunc(timeMs / unixTimesInMs.hour);
const overLastHourStormExpiry = hour * unixTimesInMs.hour + 10 * unixTimesInMs.minute;
const thisHourStormActivation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute;
if (overLastHourStormExpiry > timeMs) {
pushVoidStorms(worldState.VoidStorms, hour - 2);
}
pushVoidStorms(worldState.VoidStorms, hour - 1);
if (isBeforeNextExpectedWorldStateRefresh(timeMs, thisHourStormActivation)) {
pushVoidStorms(worldState.VoidStorms, hour);
}
// Sentient Anomaly + Xtra Cheese cycles
@ -3790,10 +3777,7 @@ export const getNightwaveSyndicateTag = (buildLabel: string | undefined): string
valid_values: Object.keys(nightwaveTagToSeason)
});
}
if (!buildLabel || version_compare(buildLabel, "2025.10.14.16.10") >= 0) {
return "RadioLegionIntermission14Syndicate";
}
if (version_compare(buildLabel, "2025.05.20.10.18") >= 0) {
if (!buildLabel || version_compare(buildLabel, "2025.05.20.10.18") >= 0) {
return "RadioLegionIntermission13Syndicate";
}
if (version_compare(buildLabel, "2025.02.05.11.19") >= 0) {
@ -3803,7 +3787,6 @@ export const getNightwaveSyndicateTag = (buildLabel: string | undefined): string
};
const nightwaveTagToSeason: Record<string, number> = {
RadioLegionIntermission14Syndicate: 16, // Nora's Mix: Dreams of the Dead
RadioLegionIntermission13Syndicate: 15, // Nora's Mix Vol. 9
RadioLegionIntermission12Syndicate: 14, // Nora's Mix Vol. 8
RadioLegionIntermission11Syndicate: 13, // Nora's Mix Vol. 7
@ -3822,10 +3805,6 @@ const nightwaveTagToSeason: Record<string, number> = {
RadioLegionSyndicate: 0 // The Wolf of Saturn Six
};
const nightwaveTagToActivation: Record<string, number> = {
RadioLegionIntermission14Syndicate: 1761589199000
};
const updateFissures = async (): Promise<void> => {
const fissures = await Fissure.find();

View File

@ -14,7 +14,6 @@ import type { IOrbiterClient } from "../personalRoomsTypes.ts";
import type { ICountedStoreItem } from "warframe-public-export-plus";
import type { IEquipmentClient, IEquipmentDatabase, ITraits } from "../equipmentTypes.ts";
import type { ILoadOutPresets } from "../saveLoadoutTypes.ts";
import type { CalendarSeasonType } from "../worldStateTypes.ts";
export type InventoryDatabaseEquipment = {
[_ in TEquipmentKey]: IEquipmentDatabase[];
@ -1181,7 +1180,7 @@ export interface IMarker {
}
export interface ISeasonProgress {
SeasonType: CalendarSeasonType;
SeasonType: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
LastCompletedDayIdx: number;
LastCompletedChallengeDayIdx: number;
ActivatedChallenges: string[];

View File

@ -32,7 +32,6 @@ export interface IWorldState {
ActiveChallenges: ISeasonChallenge[];
};
KnownCalendarSeasons: ICalendarSeason[];
Conquests?: IConquest[];
Tmp?: string;
}
@ -353,11 +352,10 @@ export interface ISeasonChallenge {
Challenge: string;
}
export type CalendarSeasonType = "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
export interface ICalendarSeason {
Activation: IMongoDate;
Expiry: IMongoDate;
Season: CalendarSeasonType;
Season: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
Days: ICalendarDay[];
YearIteration: number;
Version: number;
@ -418,33 +416,6 @@ export interface IGameMarketCategory {
Items?: string[];
}
// >= 40.0.0
export type TConquestType = "CT_LAB" | "CT_HEX";
export interface IConquest {
Activation: IMongoDate;
Expiry: IMongoDate;
Type: TConquestType;
Missions: IConquestMission[];
Variables: [string, string, string, string];
RandomSeed: number;
}
export interface IConquestMission {
faction: TFaction;
missionType: TMissionType;
difficulties: [
{
type: "CD_NORMAL";
deviation: string;
risks: [string];
},
{
type: "CD_HARD";
deviation: string;
risks: [string, string];
}
];
}
export interface ITmp {
cavabegin: string;
PurchasePlatformLockEnabled: boolean; // Seems unused
@ -452,8 +423,6 @@ export interface ITmp {
ennnd?: boolean; // True if 1999 demo is available (no effect for >=38.6.0)
mbrt?: boolean; // Related to mobile app rating request
fbst: IFbst;
lqo?: IConquestOverride;
hqo?: IConquestOverride;
sfn: number;
edg?: TCircuitGameMode[]; // The Circuit game modes overwrite
}
@ -482,12 +451,3 @@ interface IFbst {
e: number;
n: number;
}
// < 40.0.0
interface IConquestOverride {
mt?: string[]; // mission types but "Exterminate" instead of "MT_EXTERMINATION", etc. and "DualDefense" instead of "Defense" for hex conquest
mv?: string[];
mf?: number[]; // hex conquest only
c?: [string, string][];
fv?: string[];
}

View File

@ -1152,7 +1152,7 @@
<button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
<button class="btn btn-primary" onclick="debounce(addMissingHelminthRecipes);" data-loc="cheats_addMissingSubsumedAbilities"></button>
<button class="btn btn-primary" onclick="doIntrinsicsUnlockAll();" data-loc="cheats_intrinsicsUnlockAll"></button>
<button class="btn btn-primary" onclick="debounce(doMaxPlexus);" data-loc="cheats_maxPlexus"></button>
<button class="btn btn-primary" onclick="debounce(doMaxPlexus);" data-loc="inventory_maxPlexus"></button>
<button class="btn btn-primary" onclick="debounce(unlockAllProfitTakerStages);" data-loc="cheats_unlockAllProfitTakerStages"></button>
<button class="btn btn-primary" onclick="debounce(unlockAllSimarisResearchEntries);" data-loc="cheats_unlockAllSimarisResearchEntries"></button>
</div>
@ -1474,7 +1474,6 @@
<label class="form-label" for="worldState.nightwaveOverride" data-loc="worldState_nightwaveOverride"></label>
<select class="form-control" id="worldState.nightwaveOverride" data-default="">
<option value="" data-loc="disabled"></option>
<option value="RadioLegionIntermission14Syndicate" data-loc="worldState_RadioLegionIntermission14Syndicate"></option>
<option value="RadioLegionIntermission13Syndicate" data-loc="worldState_RadioLegionIntermission13Syndicate"></option>
<option value="RadioLegionIntermission12Syndicate" data-loc="worldState_RadioLegionIntermission12Syndicate"></option>
<option value="RadioLegionIntermission11Syndicate" data-loc="worldState_RadioLegionIntermission11Syndicate"></option>

View File

@ -1542,17 +1542,14 @@ function updateInventory() {
if (item) {
document.getElementById("detailedView-loading").classList.add("d-none");
const itemName = itemMap[item.ItemType]?.name ?? item.ItemType;
if (item.ItemName) {
const pipeIndex = item.ItemName.indexOf("|");
if (pipeIndex != -1) {
$("#detailedView-title").text(item.ItemName.substr(1 + pipeIndex) + " " + itemName);
} else {
$("#detailedView-title").text(item.ItemName);
$("#detailedView-route .text-body-secondary").text(itemName);
}
$("#detailedView-title").text(item.ItemName);
$("#detailedView-route .text-body-secondary").text(
itemMap[item.ItemType]?.name ?? item.ItemType
);
} else {
$("#detailedView-title").text(itemName);
$("#detailedView-title").text(itemMap[item.ItemType]?.name ?? item.ItemType);
}
if (category == "Suits") {

View File

@ -1,6 +1,6 @@
// German translation by Animan8000
dict = {
general_inventoryUpdateNote: `Hinweis: Um Änderungen im Spiel zu sehen, musst du dein Inventar neu synchronisieren, z. B. durch Besuch eines Dojo/Relais oder durch erneutes Anmelden.`,
general_inventoryUpdateNote: `Hinweis: Um Änderungen im Spiel zu sehen, musst du dein Inventar neu synchronisieren, z. B. mit dem /sync Befehl des Bootstrappers im Spielchat, durch Besuch eines Dojo/Relais oder durch erneutes Einloggen.`,
general_inventoryUpdateNoteGameWs: `Hinweis: Möglicherweise musst du ein Menü neu öffnen, damit die Änderungen sichtbar werden.`,
general_addButton: `Hinzufügen`,
general_setButton: `Festlegen`,
@ -51,7 +51,7 @@ dict = {
code_focusUnlocked: `|COUNT| neue Fokus-Schulen freigeschaltet! Ein Inventar-Update wird benötigt, damit die Änderungen im Spiel sichtbar werden.`,
code_addModsConfirm: `Bist du sicher, dass du |COUNT| Mods zu deinem Account hinzufügen möchtest?`,
code_succImport: `Erfolgreich importiert.`,
code_succRelog: `Fertig. Bitte beachte, dass du dich neu anmelden musst, um Änderungen im Spiel zu sehen.`,
code_succRelog: `Fertig. Bitte beachte, dass du dich neu einloggen musst, um Änderungen im Spiel zu sehen.`,
code_nothingToDo: `Fertig. Es gab nichts zu tun.`,
code_gild: `Veredeln`,
code_moa: `Moa`,
@ -134,7 +134,8 @@ dict = {
inventory_bulkRankUpSentinels: `Alle Wächter auf Max. Rang`,
inventory_bulkRankUpSentinelWeapons: `Alle Wächter-Waffen auf Max. Rang`,
inventory_bulkRankUpEvolutionProgress: `Alle Incarnon-Entwicklungsfortschritte auf Max. Rang`,
inventory_removeIsNew: `Entferne Ausrufezeichen bei neuem Equipment`,
inventory_maxPlexus: `Plexus auf Max. Rang`,
inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
quests_list: `Quests`,
quests_completeAll: `Alle Quests abschließen`,
@ -199,9 +200,9 @@ dict = {
cheats_skipTutorial: `Tutorial überspringen`,
cheats_skipAllDialogue: `Alle Dialoge überspringen`,
cheats_unlockAllScans: `Alle Scans freischalten`,
cheats_unlockSuccRelog: `Erfolgreich. Bitte beachte, dass du dich neu anmelden musst, damit der Client dies aktualisiert.`,
cheats_unlockSuccRelog: `Erfolgreich. Bitte beachte, dass du dich neu einloggen musst, damit der Client dies aktualisiert.`,
cheats_unlockAllMissions: `Alle Missionen freischalten`,
cheats_unlockAllMissions_ok: `Erfolgreich. Bitte beachte, dass du ein Dojo/Relais besuchen oder dich neu anmelden 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_infinitePlatinum: `Unendlich Platinum`,
cheats_infiniteEndo: `Unendlich Endo`,
@ -213,7 +214,7 @@ dict = {
cheats_dontSubtractPurchaseItemCost: `Gegenstände beim Kauf nicht verbrauchen`,
cheats_dontSubtractPurchaseStandingCost: `Ansehen beim Kauf nicht verbrauchen`,
cheats_dontSubtractVoidTraces: `Void-Spuren nicht verbrauchen`,
cheats_dontSubtractConsumables: `Verbrauchsgüter (Ausrüstung) nicht verbrauchen`,
cheats_dontSubtractConsumables: `Verbrauchsgegenstände (Ausrüstung) nicht verbrauchen`,
cheats_unlockAllShipFeatures: `Alle Schiffs-Funktionen freischalten`,
cheats_unlockAllSkins: `Alle Skins freischalten`,
cheats_unlockAllCapturaScenes: `Alle Photora-Szenen freischalten`,
@ -233,7 +234,7 @@ dict = {
cheats_baroFullyStocked: `Baro hat volles Inventar`,
cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
cheats_unlockAllProfitTakerStages: `Alle Profiteintreiber-Phasen freischalten`,
cheats_unlockSuccInventory: `Erfolgreich. Bitte beachte, dass du dein Inventar neu synchronisieren musst, z. B. durch Besuch eines Dojo/Relais oder durch erneutes Anmelden.`,
cheats_unlockSuccInventory: `Erfolgreich. Bitte beachte, dass du dein Inventar neu synchronisieren musst, z. B. mit dem /sync Befehl des Bootstrappers im Spielchat, durch Besuch eines Dojo/Relais oder durch erneutes Einloggen.`,
cheats_instantFinishRivenChallenge: `Riven-Mod Herausforderung sofort abschließen`,
cheats_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`,
cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`,
@ -259,7 +260,6 @@ dict = {
cheats_helminthUnlockAll: `Helminth vollständig aufleveln`,
cheats_addMissingSubsumedAbilities: `Fehlende konsumierte Fähigkeiten hinzufügen`,
cheats_intrinsicsUnlockAll: `Alle Inhärenzen auf Max. Rang`,
cheats_maxPlexus: `Plexus auf Max. Rang`,
cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
cheats_changeButton: `Ändern`,
cheats_markAllAsRead: `Posteingang als gelesen markieren`,
@ -324,7 +324,6 @@ dict = {
worldState_sorrow: `Trauer`,
worldState_fear: `Angst`,
worldState_nightwaveOverride: `Nightwave-Überschreibung`,
worldState_RadioLegionIntermission14Syndicate: `Noras Mix: Träume der Toten`,
worldState_RadioLegionIntermission13Syndicate: `Noras Mix - Vol. 9`,
worldState_RadioLegionIntermission12Syndicate: `Noras Mix - Vol. 8`,
worldState_RadioLegionIntermission11Syndicate: `Noras Mix - Vol. 7`,
@ -386,10 +385,10 @@ dict = {
upgrade_AvatarAbilityRange: `+7.5% Fähigkeitsreichweite`,
upgrade_AvatarAbilityEfficiency: `+5% Fähigkeitseffizienz`,
upgrade_AvatarEnergyRegen: `+0.5 Energieregeneration pro Sekunde`,
upgrade_AvatarEnemyRadar: `+5m Gegnerradar`,
upgrade_AvatarEnemyRadar: `+5m Feindradar`,
upgrade_AvatarLootRadar: `+7m Beuteradar`,
upgrade_WeaponAmmoMax: `+15% Max. Munition`,
upgrade_EnemyArmorReductionAura: `-3% Rüstung für Gegner`,
upgrade_EnemyArmorReductionAura: `-3% Rüstung bei Feinden`,
upgrade_OnExecutionAmmo: `+100% Magazinfüllung für Primär- und Sekundärwaffen bei Gnadenstoß`,
upgrade_OnExecutionHealthDrop: `+100% Gesundheitskugel Chance bei Gnadenstoß`,
upgrade_OnExecutionEnergyDrop: `+50% Energiekugel Chance bei Gnadenstoß`,
@ -399,7 +398,7 @@ dict = {
upgrade_OnExecutionParkourSpeed: `+60% Parkourgeschwindigkeit für 15s nach Gnadenstoß`,
upgrade_AvatarTimeLimitIncrease: `+8s extra Zeit beim Hacken`,
upgrade_ElectrifyOnHack: `Setze beim Hacken Gegner innerhalb von 20m unter Strom`,
upgrade_OnExecutionTerrify: `+50% Chance bei Gnadenstoß, dass Gegner innerhalb von 15m vor Furcht für 8s kauern`,
upgrade_OnExecutionTerrify: `+50% Chance bei Gnadenstoß, dass Feinde innerhalb von 15m vor Furcht für 8s kauern`,
upgrade_OnHackLockers: `Schließe nach dem Hacken 5 Spinde innerhalb von 20m auf`,
upgrade_OnExecutionBlind: `Blende bei einem Gnadenstoß Gegner innerhalb von 18m`,
upgrade_OnExecutionDrainPower: `Nächste Fähigkeit erhält +50% Fähigkeitsstärke nach Gnadenstoß`,

View File

@ -1,5 +1,5 @@
dict = {
general_inventoryUpdateNote: `Note: To see changes in-game, you need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
general_inventoryUpdateNote: `Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command in game chat, visiting a dojo/relay, or relogging.`,
general_inventoryUpdateNoteGameWs: `Note: You may need to reopen any menu you are on for changes to be reflected.`,
general_addButton: `Add`,
general_setButton: `Set`,
@ -133,6 +133,7 @@ dict = {
inventory_bulkRankUpSentinels: `Max Rank All Sentinels`,
inventory_bulkRankUpSentinelWeapons: `Max Rank All Sentinel Weapons`,
inventory_bulkRankUpEvolutionProgress: `Max Rank All Incarnon Evolution Progress`,
inventory_maxPlexus: `Max Rank Plexus`,
inventory_removeIsNew: `Remove New Equipment Exclamation Icon`,
quests_list: `Quests`,
@ -232,7 +233,7 @@ dict = {
cheats_baroFullyStocked: `Baro Fully Stocked`,
cheats_syndicateMissionsRepeatable: `Syndicate Missions Repeatable`,
cheats_unlockAllProfitTakerStages: `Unlock All Profit Taker Stages`,
cheats_unlockSuccInventory: `Success. Please note that you'll need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
cheats_unlockSuccInventory: `Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command in game chat, visiting a dojo/relay, or relogging.`,
cheats_instantFinishRivenChallenge: `Instant Finish Riven Challenge`,
cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
cheats_noResourceExtractorDronesDamage: `No Resource Extractor Drones Damage`,
@ -258,7 +259,6 @@ dict = {
cheats_helminthUnlockAll: `Fully Level Up Helminth`,
cheats_addMissingSubsumedAbilities: `Add Missing Subsumed Abilities`,
cheats_intrinsicsUnlockAll: `Max Rank All Intrinsics`,
cheats_maxPlexus: `Max Rank Plexus`,
cheats_changeSupportedSyndicate: `Supported syndicate`,
cheats_changeButton: `Change`,
cheats_markAllAsRead: `Mark Inbox As Read`,
@ -323,7 +323,6 @@ dict = {
worldState_sorrow: `Sorrow`,
worldState_fear: `Fear`,
worldState_nightwaveOverride: `Nightwave Override`,
worldState_RadioLegionIntermission14Syndicate: `Nora's Mix: Dreams of the Dead`,
worldState_RadioLegionIntermission13Syndicate: `Nora's Mix Vol. 9`,
worldState_RadioLegionIntermission12Syndicate: `Nora's Mix Vol. 8`,
worldState_RadioLegionIntermission11Syndicate: `Nora's Mix Vol. 7`,

View File

@ -1,6 +1,6 @@
// Spanish translation by hxedcl, Slayer55555
dict = {
general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
general_inventoryUpdateNote: `Para ver los cambios en el juego, necesitas volver a sincronizar tu inventario, por ejemplo, usando el comando /sync del bootstrapper, visitando un dojo o repetidor, o volviendo a iniciar sesión.`,
general_inventoryUpdateNoteGameWs: `Nota: Puede que necesites reabrir cualquier menú en el que te encuentres para que los cambios se reflejen.`,
general_addButton: `Agregar`,
general_setButton: `Establecer`,
@ -134,6 +134,7 @@ dict = {
inventory_bulkRankUpSentinels: `Maximizar rango de todos los centinelas`,
inventory_bulkRankUpSentinelWeapons: `Maximizar rango de todas las armas de centinela`,
inventory_bulkRankUpEvolutionProgress: `Maximizar todo el progreso de evolución Incarnon`,
inventory_maxPlexus: `Rango máximo de Plexus`,
inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
quests_list: `Misiones`,
@ -233,7 +234,7 @@ dict = {
cheats_baroFullyStocked: `Baro con stock completo`,
cheats_syndicateMissionsRepeatable: `Misiones de sindicato rejugables`,
cheats_unlockAllProfitTakerStages: `Desbloquea todas las etapas del Roba-ganancias`,
cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
cheats_unlockSuccInventory: `Éxito. Ten en cuenta que deberás volver a sincronizar tu inventario. Para hacerlo, puedes usar el comando /sync en el Bootstrapper, visitar un dojo o repetidor, o volver a iniciar sesión.`,
cheats_instantFinishRivenChallenge: `Terminar desafío de agrietado inmediatamente`,
cheats_instantResourceExtractorDrones: `Drones de extracción de recursos instantáneos`,
cheats_noResourceExtractorDronesDamage: `Sin daño a los drones extractores de recursos`,
@ -259,7 +260,6 @@ dict = {
cheats_helminthUnlockAll: `Subir al máximo el Helminto`,
cheats_addMissingSubsumedAbilities: `Agregar habilidades subsumidas faltantes`,
cheats_intrinsicsUnlockAll: `Maximizar todos los intrínsecos`,
cheats_maxPlexus: `Rango máximo de Plexus`,
cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
cheats_changeButton: `Cambiar`,
cheats_markAllAsRead: `Marcar bandeja de entrada como leída`,
@ -324,7 +324,6 @@ dict = {
worldState_sorrow: `Tristeza`,
worldState_fear: `Miedo`,
worldState_nightwaveOverride: `Volúmen de Onda Nocturna`,
worldState_RadioLegionIntermission14Syndicate: `[UNTRANSLATED] Nora's Mix: Dreams of the Dead`,
worldState_RadioLegionIntermission13Syndicate: `Mix de Nora Vol. 9`,
worldState_RadioLegionIntermission12Syndicate: `Mix de Nora Vol. 8`,
worldState_RadioLegionIntermission11Syndicate: `Mix de Nora Vol. 7`,

View File

@ -1,6 +1,6 @@
// French translation by Vitruvio
dict = {
general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
general_inventoryUpdateNote: `Note : Pour voir les changements en jeu, l'inventaire doit être actualisé. Cela se fait en tapant /sync dans le tchat, en visitant un dojo/relais ou en se reconnectant.`,
general_inventoryUpdateNoteGameWs: `Note : Rouvrir un menu est nécessaire pour voir les changements.`,
general_addButton: `Ajouter`,
general_setButton: `Définir`,
@ -134,6 +134,7 @@ dict = {
inventory_bulkRankUpSentinels: `Toutes les Sentinelles au rang max`,
inventory_bulkRankUpSentinelWeapons: `Toutes les armes de Sentinelles au rang max`,
inventory_bulkRankUpEvolutionProgress: `Toutes les évolutions Incarnon au rang max`,
inventory_maxPlexus: `Plexus au rang max`,
inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
quests_list: `Quêtes`,
@ -233,7 +234,7 @@ dict = {
cheats_baroFullyStocked: `Stock de Baro au max`,
cheats_syndicateMissionsRepeatable: `Mission syndicat répétables`,
cheats_unlockAllProfitTakerStages: `Débloquer toutes les étapes du Preneur de Profit`,
cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
cheats_unlockSuccInventory: `Succès. Une resynchronisation est nécessaire en tapant "/sync" dans le tchat, en visitant un relais ou en se reconnectant.`,
cheats_instantFinishRivenChallenge: `Débloquer le challenge Riven instantanément`,
cheats_instantResourceExtractorDrones: `Ressources de drones d'extraction instantannées`,
cheats_noResourceExtractorDronesDamage: `Aucun dégâts aux drones d'extraction de resources`,
@ -259,7 +260,6 @@ dict = {
cheats_helminthUnlockAll: `Helminth niveau max`,
cheats_addMissingSubsumedAbilities: `Ajouter les capacités subsumées manquantes`,
cheats_intrinsicsUnlockAll: `Inhérences niveau max`,
cheats_maxPlexus: `Plexus au rang max`,
cheats_changeSupportedSyndicate: `Allégeance`,
cheats_changeButton: `Changer`,
cheats_markAllAsRead: `Marquer la boîte de réception comme lue`,
@ -324,7 +324,6 @@ dict = {
worldState_sorrow: `hagrin`,
worldState_fear: `Peur`,
worldState_nightwaveOverride: `Saison d'Ondes Nocturnes`,
worldState_RadioLegionIntermission14Syndicate: `[UNTRANSLATED] Nora's Mix: Dreams of the Dead`,
worldState_RadioLegionIntermission13Syndicate: `Mix de Nora Vol. 9`,
worldState_RadioLegionIntermission12Syndicate: `Mix de Nora Vol. 8`,
worldState_RadioLegionIntermission11Syndicate: `Mix de Nora Vol. 7`,

View File

@ -1,6 +1,6 @@
// Russian translation by AMelonInsideLemon, LoseFace
dict = {
general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
general_inventoryUpdateNote: `Примечание: Чтобы увидеть изменения в игре, вам нужно повторно синхронизировать свой инвентарь, например, используя команду загрузчика /sync в чате игры, посетив Додзё/Реле или перезагрузив игру.`,
general_inventoryUpdateNoteGameWs: `Примечание: для того, чтобы изменения вступили в силу, может потребоваться повторно открыть меню, в котором вы находитесь.`,
general_addButton: `Добавить`,
general_setButton: `Установить`,
@ -134,6 +134,7 @@ dict = {
inventory_bulkRankUpSentinels: `Макс. ранг всех Стражей`,
inventory_bulkRankUpSentinelWeapons: `Макс. ранг всего оружия Стражей`,
inventory_bulkRankUpEvolutionProgress: `Макс. ранг всех эволюций Инкарнонов`,
inventory_maxPlexus: `Макс. ранг Плексуса`,
inventory_removeIsNew: `Удалить значок восклицательного знака нового снаряжения`,
quests_list: `Квесты`,
@ -233,7 +234,7 @@ dict = {
cheats_baroFullyStocked: `Баро полностью укомплектован`,
cheats_syndicateMissionsRepeatable: `Повторять миссии синдиката`,
cheats_unlockAllProfitTakerStages: `Разблокировать все этапы Сферы извлечения прибыли`,
cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
cheats_unlockSuccInventory: `Успех. Обратите внимание, что вам необходимо будет повторно синхронизировать свой инвентарь, например, с помощью команды загрузчика /sync в чате игры, посетив Додзё/Реле или повторно войдя в игру.`,
cheats_instantFinishRivenChallenge: `Мгновенное завершение испытания мода Разлома`,
cheats_instantResourceExtractorDrones: `Мгновенно добывающие Дроны-сборщики`,
cheats_noResourceExtractorDronesDamage: `Без урона по Дронам-сборщикам`,
@ -259,7 +260,6 @@ dict = {
cheats_helminthUnlockAll: `Полностью улучшить Гельминта`,
cheats_addMissingSubsumedAbilities: `Добавить отсутствующие поглощённые способности`,
cheats_intrinsicsUnlockAll: `Полностью улучшить Модуляры`,
cheats_maxPlexus: `Макс. ранг Плексуса`,
cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
cheats_changeButton: `Изменить`,
cheats_markAllAsRead: `Пометить все входящие как прочитанные`,
@ -324,7 +324,6 @@ dict = {
worldState_sorrow: `Печаль`,
worldState_fear: `Страх`,
worldState_nightwaveOverride: `Сезон Ночной волны`,
worldState_RadioLegionIntermission14Syndicate: `[UNTRANSLATED] Nora's Mix: Dreams of the Dead`,
worldState_RadioLegionIntermission13Syndicate: `Микс Норы, Диск 9`,
worldState_RadioLegionIntermission12Syndicate: `Микс Норы, Диск 8`,
worldState_RadioLegionIntermission11Syndicate: `Микс Норы, Диск 7`,

View File

@ -1,6 +1,6 @@
// Ukrainian translation by LoseFace
dict = {
general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
general_inventoryUpdateNote: `Пам'ятка: Щоб побачити зміни в грі, вам потрібно повторно синхронізувати своє спорядження, наприклад, використовуючи команду завантажувача /sync у чаті гри, відвідавши Доджьо/Реле або перезавантаживши гру.`,
general_inventoryUpdateNoteGameWs: `Примітка: для відображення змін може знадобитися повторно відкрити меню, в якому ви перебуваєте.`,
general_addButton: `Добавити`,
general_setButton: `Встановити`,
@ -134,6 +134,7 @@ dict = {
inventory_bulkRankUpSentinels: `Макс. рівень всіх Вартових`,
inventory_bulkRankUpSentinelWeapons: `Макс. рівень всієї зброї Вартових`,
inventory_bulkRankUpEvolutionProgress: `Макс. рівень всіх еволюцій Інкарнонів`,
inventory_maxPlexus: `Макс. рівень Плексу`,
inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
quests_list: `Пригоди`,
@ -233,7 +234,7 @@ dict = {
cheats_baroFullyStocked: `Баро повністю укомплектований`,
cheats_syndicateMissionsRepeatable: `Повторювати місії синдиката`,
cheats_unlockAllProfitTakerStages: `Розблокувати всі етапи Привласнювачки`,
cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
cheats_unlockSuccInventory: `Успішно. Зверніть увагу, що вам потрібно буде повторно синхронізувати своє спорядження, наприклад, за допомогою команди завантажувача /sync у чаті гри, відвідавши Доджьо/Реле або повторно увійшовши в гру.`,
cheats_instantFinishRivenChallenge: `Миттєве завершення випробування модифікатора Розколу`,
cheats_instantResourceExtractorDrones: `Миттєво добуваючі Дрони-видобувачі`,
cheats_noResourceExtractorDronesDamage: `Без шкоди по Дронам-видобувачам`,
@ -259,7 +260,6 @@ dict = {
cheats_helminthUnlockAll: `Повністю покращити Гельмінта`,
cheats_addMissingSubsumedAbilities: `Додати відсутні поглинуті здібності`,
cheats_intrinsicsUnlockAll: `Повністю покращити Кваліфікації`,
cheats_maxPlexus: `Макс. рівень Плексу`,
cheats_changeSupportedSyndicate: `Підтримуваний синдикат`,
cheats_changeButton: `Змінити`,
cheats_markAllAsRead: `Помітити всі вхідні як прочитані`,
@ -324,7 +324,6 @@ dict = {
worldState_sorrow: `Журба`,
worldState_fear: `Страх`,
worldState_nightwaveOverride: `Сезон Нічної хвилі`,
worldState_RadioLegionIntermission14Syndicate: `[UNTRANSLATED] Nora's Mix: Dreams of the Dead`,
worldState_RadioLegionIntermission13Syndicate: `Вибірка Нори 9`,
worldState_RadioLegionIntermission12Syndicate: `Вибірка Нори 8`,
worldState_RadioLegionIntermission11Syndicate: `Вибірка Нори 7`,

View File

@ -1,6 +1,6 @@
// Chinese translation by meb154, bishan178, nyaoouo, qianlishun, CrazyZhang, Corvus, qingchun
dict = {
general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
general_inventoryUpdateNote: `注意: 要在游戏中查看更改,您需要重新同步库存,例如使用客户端的 /sync 命令,访问道场/中继站或重新登录.`,
general_inventoryUpdateNoteGameWs: `[UNTRANSLATED] Note: You may need to reopen any menu you are on for changes to be reflected.`,
general_addButton: `添加`,
general_setButton: `设置`,
@ -134,6 +134,7 @@ dict = {
inventory_bulkRankUpSentinels: `所有守护升满级`,
inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`,
inventory_bulkRankUpEvolutionProgress: `所有灵化之源进度最大等级`,
inventory_maxPlexus: `最大深控等级`,
inventory_removeIsNew: `[UNTRANSLATED] Remove New Equipment Exclamation Icon`,
quests_list: `系列任务`,
@ -233,7 +234,7 @@ dict = {
cheats_baroFullyStocked: `虚空商人贩卖所有商品`,
cheats_syndicateMissionsRepeatable: `集团任务可重复完成`,
cheats_unlockAllProfitTakerStages: `解锁利润收割者圆蛛所有阶段`,
cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. by visiting a dojo/relay or relogging.`,
cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command in game chat, visiting a dojo/relay, or relogging.`,
cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`,
cheats_instantResourceExtractorDrones: `资源无人机即时完成`,
cheats_noResourceExtractorDronesDamage: `资源无人机不会损毁`,
@ -259,7 +260,6 @@ dict = {
cheats_helminthUnlockAll: `完全升级Helminth`,
cheats_addMissingSubsumedAbilities: `添加Helminth未汲取的战甲技能`,
cheats_intrinsicsUnlockAll: `所有内源之力最大等级`,
cheats_maxPlexus: `最大深控等级`,
cheats_changeSupportedSyndicate: `支持的集团`,
cheats_changeButton: `更改`,
cheats_markAllAsRead: `收件箱全部标记为已读`,
@ -324,7 +324,6 @@ dict = {
worldState_sorrow: `悲伤`,
worldState_fear: `恐惧`,
worldState_nightwaveOverride: `午夜电波系列`,
worldState_RadioLegionIntermission14Syndicate: `[UNTRANSLATED] Nora's Mix: Dreams of the Dead`,
worldState_RadioLegionIntermission13Syndicate: `诺拉的混选VOL.9`,
worldState_RadioLegionIntermission12Syndicate: `诺拉的混选VOL.8`,
worldState_RadioLegionIntermission11Syndicate: `诺拉的混选VOL.7`,