merge upstream

This commit is contained in:
hxedcl 2025-04-22 18:47:03 -07:00
commit eeb3e687cd
20 changed files with 246 additions and 168 deletions

View File

@ -26,7 +26,7 @@ app.use((req, _res, next) => {
app.use(bodyParser.raw());
app.use(express.json({ limit: "4mb" }));
app.use(bodyParser.text());
app.use(bodyParser.text({ limit: "4mb" }));
app.use(requestLogger);
app.use("/api", apiRouter);

View File

@ -14,7 +14,12 @@ import {
ExportVirtuals
} from "warframe-public-export-plus";
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
import { addMiscItems, allDailyAffiliationKeys, createLibraryDailyTask } from "@/src/services/inventoryService";
import {
addMiscItems,
allDailyAffiliationKeys,
cleanupInventory,
createLibraryDailyTask
} from "@/src/services/inventoryService";
import { logger } from "@/src/utils/logger";
import { catBreadHash } from "@/src/helpers/stringHelpers";
@ -79,6 +84,8 @@ export const inventoryController: RequestHandler = async (request, response) =>
}
}
cleanupInventory(inventory);
inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000);
await inventory.save();
}

View File

@ -12,22 +12,17 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
const request = JSON.parse(String(req.body)) as SaveDialogueRequest;
if ("YearIteration" in request) {
const inventory = await getInventory(accountId, "DialogueHistory");
if (inventory.DialogueHistory) {
inventory.DialogueHistory ??= {};
inventory.DialogueHistory.YearIteration = request.YearIteration;
} else {
inventory.DialogueHistory = { YearIteration: request.YearIteration };
}
await inventory.save();
res.end();
} else {
const inventory = await getInventory(accountId);
if (!inventory.DialogueHistory) {
throw new Error("bad inventory state");
}
const inventoryChanges: IInventoryChanges = {};
const tomorrowAt0Utc = config.noKimCooldowns
? Date.now()
: (Math.trunc(Date.now() / 86400_000) + 1) * 86400_000;
inventory.DialogueHistory ??= {};
inventory.DialogueHistory.Dialogues ??= [];
const dialogue = getDialogue(inventory, request.DialogueName);
dialogue.Rank = request.Rank;

View File

@ -29,6 +29,7 @@ interface ListedItem {
badReason?: "starter" | "frivolous" | "notraw";
partType?: string;
chainLength?: number;
parazon?: boolean;
}
const relicQualitySuffixes: Record<TRelicQuality, string> = {
@ -68,7 +69,8 @@ const getItemListsController: RequestHandler = (req, response) => {
if (item.productCategory != "SpecialItems") {
res[item.productCategory].push({
uniqueName,
name: getString(item.name, lang)
name: getString(item.name, lang),
exalted: item.exalted
});
}
}
@ -196,6 +198,9 @@ const getItemListsController: RequestHandler = (req, response) => {
} else if (upgrade.upgradeEntries) {
mod.badReason = "notraw";
}
if (upgrade.type == "PARAZON") {
mod.parazon = true;
}
res.mods.push(mod);
}
for (const [uniqueName, upgrade] of Object.entries(ExportAvionics)) {

View File

@ -909,7 +909,7 @@ dialogueSchema.set("toJSON", {
const dialogueHistorySchema = new Schema<IDialogueHistoryDatabase>(
{
YearIteration: { type: Number, required: true },
YearIteration: Number,
Resets: Number,
Dialogues: { type: [dialogueSchema], required: false }
},

View File

@ -37,6 +37,7 @@ import {
} from "../types/inventoryTypes/inventoryTypes";
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "../types/saveLoadoutTypes";
import { slotNames } from "../types/purchaseTypes";
const convertDate = (value: IMongoDate): Date => {
return new Date(parseInt(value.$date.$numberLong));
@ -212,20 +213,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
}
}
for (const key of [
"SuitBin",
"WeaponBin",
"SentinelBin",
"SpaceSuitBin",
"SpaceWeaponBin",
"PvpBonusLoadoutBin",
"PveBonusLoadoutBin",
"RandomModBin",
"MechBin",
"CrewMemberBin",
"OperatorAmpBin",
"CrewShipSalvageBin"
] as const) {
for (const key of slotNames) {
if (client[key] !== undefined) {
replaceSlots(db[key], client[key]);
}

View File

@ -87,9 +87,7 @@ export const createInventory = async (
const inventory = new Inventory({
accountOwnerId: accountOwnerId,
LoadOutPresets: defaultItemReferences.loadOutPresetId,
Ships: [defaultItemReferences.ship],
PlayedParkourTutorial: config.skipTutorial,
ReceivedStartingGear: config.skipTutorial
Ships: [defaultItemReferences.ship]
});
inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
@ -102,6 +100,7 @@ export const createInventory = async (
await addItem(inventory, "/Lotus/Types/Friendly/PlayerControllable/Weapons/DuviriDualSwords");
if (config.skipTutorial) {
inventory.PlayedParkourTutorial = true;
await addStartingGear(inventory);
await completeQuest(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain");
@ -1769,3 +1768,23 @@ export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void
Tag: "KahlSyndicate"
});
};
export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void => {
let index = inventory.MiscItems.findIndex(x => x.ItemType == "");
if (index != -1) {
inventory.MiscItems.splice(index, 1);
}
index = inventory.Affiliations.findIndex(x => x.Tag == "KahlSyndicate");
if (index != -1 && !inventory.Affiliations[index].WeeklyMissions) {
logger.debug(`KahlSyndicate seems broken, removing it and setting up again`);
inventory.Affiliations.splice(index, 1);
setupKahlSyndicate(inventory);
}
const LibrarySyndicate = inventory.Affiliations.find(x => x.Tag == "LibrarySyndicate");
if (LibrarySyndicate && LibrarySyndicate.FreeFavorsEarned) {
logger.debug(`removing FreeFavorsEarned from LibrarySyndicate`);
LibrarySyndicate.FreeFavorsEarned = undefined;
}
};

View File

@ -55,6 +55,7 @@ import { Loadout } from "../models/inventoryModels/loadoutModel";
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
import { config } from "./configService";
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
// For Spy missions, e.g. 3 vaults cracked = A, B, C
@ -331,10 +332,10 @@ export const addMissionInventoryUpdates = async (
case "LibraryScans":
value.forEach(scan => {
let synthesisIgnored = true;
if (
inventory.LibraryPersonalTarget &&
libraryPersonalTargetToAvatar[inventory.LibraryPersonalTarget] == scan.EnemyType
) {
if (inventory.LibraryPersonalTarget) {
const taskAvatar = libraryPersonalTargetToAvatar[inventory.LibraryPersonalTarget];
const taskAvatars = libraryDailyTasks.find(x => x.indexOf(taskAvatar) != -1)!;
if (taskAvatars.indexOf(scan.EnemyType) != -1) {
let progress = inventory.LibraryPersonalProgress.find(
x => x.TargetType == inventory.LibraryPersonalTarget
);
@ -361,6 +362,7 @@ export const addMissionInventoryUpdates = async (
logger.debug(`synthesis of ${scan.EnemyType} added to personal target progress`);
synthesisIgnored = false;
}
}
if (
inventory.LibraryActiveDailyTaskInfo &&
inventory.LibraryActiveDailyTaskInfo.EnemyTypes.find(x => x == scan.EnemyType)

View File

@ -4,14 +4,7 @@ import { unixTimesInMs } from "@/src/constants/timeConstants";
import { config } from "@/src/services/configService";
import { CRng } from "@/src/services/rngService";
import { eMissionType, ExportNightwave, ExportRegions } from "warframe-public-export-plus";
import {
ICalendarDay,
ICalendarSeason,
ILiteSortie,
ISeasonChallenge,
ISortie,
IWorldState
} from "../types/worldStateTypes";
import { ICalendarDay, ICalendarSeason, ILiteSortie, ISeasonChallenge, IWorldState } from "../types/worldStateTypes";
const sortieBosses = [
"SORTIE_BOSS_HYENA",
@ -174,7 +167,47 @@ const getSortieTime = (day: number): number => {
return dayStart + (isDst ? 16 : 17) * 3600000;
};
const pushSortieIfRelevant = (out: ISortie[], day: number): void => {
const pushSyndicateMissions = (
worldState: IWorldState,
day: number,
seed: number,
idSuffix: string,
syndicateTag: string
): void => {
const nodeOptions: string[] = [];
for (const [key, value] of Object.entries(ExportRegions)) {
if (
value.name.indexOf("Archwing") == -1 && // no archwing
value.systemIndex != 23 && // no 1999 stuff
value.missionIndex != 10 && // Exclude MT_PVP (for relays)
value.missionIndex != 23 && // no junctions
value.missionIndex <= 28 // no railjack or some such
) {
nodeOptions.push(key);
}
}
const rng = new CRng(seed);
const nodes: string[] = [];
for (let i = 0; i != 6; ++i) {
const index = rng.randomInt(0, nodeOptions.length - 1);
nodes.push(nodeOptions[index]);
nodeOptions.splice(index, 1);
}
const dayStart = getSortieTime(day);
const dayEnd = getSortieTime(day + 1);
worldState.SyndicateMissions.push({
_id: { $oid: Math.trunc(dayStart / 1000).toString(16) + idSuffix },
Activation: { $date: { $numberLong: dayStart.toString() } },
Expiry: { $date: { $numberLong: dayEnd.toString() } },
Tag: syndicateTag,
Seed: seed,
Nodes: nodes
});
};
const pushSortieIfRelevant = (worldState: IWorldState, day: number): void => {
const dayStart = getSortieTime(day);
if (!isBeforeNextExpectedWorldStateRefresh(dayStart)) {
return;
@ -231,7 +264,9 @@ const pushSortieIfRelevant = (out: ISortie[], day: number): void => {
value.name.indexOf("Archwing") == -1 &&
value.missionIndex != 0 && // Exclude MT_ASSASSINATION
value.missionIndex != 5 && // Exclude MT_CAPTURE
value.missionIndex != 10 && // Exclude MT_PVP (for relays)
value.missionIndex != 21 && // Exclude MT_PURIFY
value.missionIndex != 22 && // Exclude MT_ARENA
value.missionIndex != 23 && // Exclude MT_JUNCTION
value.missionIndex <= 28
) {
@ -291,7 +326,7 @@ const pushSortieIfRelevant = (out: ISortie[], day: number): void => {
missionTypes.add(missionType);
}
out.push({
worldState.Sorties.push({
_id: { $oid: Math.trunc(dayStart / 1000).toString(16) + "d4d932c97c0a3acd" },
Activation: { $date: { $numberLong: dayStart.toString() } },
Expiry: { $date: { $numberLong: dayEnd.toString() } },
@ -300,6 +335,13 @@ const pushSortieIfRelevant = (out: ISortie[], day: number): void => {
Boss: boss,
Variants: selectedNodes
});
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48049", "ArbitersSyndicate");
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804a", "CephalonSudaSyndicate");
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4804e", "NewLokaSyndicate");
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48050", "PerrinSyndicate");
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa4805e", "RedVeilSyndicate");
pushSyndicateMissions(worldState, day, rng.randomInt(0, 0xffff), "ba6f84724fa48061", "SteelMeridianSyndicate");
};
const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
@ -952,9 +994,9 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
});
}
// Sortie cycling every day
pushSortieIfRelevant(worldState.Sorties, day - 1);
pushSortieIfRelevant(worldState.Sorties, day);
// Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
pushSortieIfRelevant(worldState, day - 1);
pushSortieIfRelevant(worldState, day);
// Archon Hunt cycling every week
worldState.LiteSorties.push(getLiteSortie(week));

View File

@ -1130,13 +1130,13 @@ export interface IEndlessXpProgress {
}
export interface IDialogueHistoryClient {
YearIteration: number;
YearIteration?: number;
Resets?: number; // added in 38.5.0
Dialogues?: IDialogueClient[];
}
export interface IDialogueHistoryDatabase {
YearIteration: number;
YearIteration?: number;
Resets?: number;
Dialogues?: IDialogueDatabase[];
}

View File

@ -103,6 +103,7 @@ export const slotNames = [
"WeaponBin",
"MechBin",
"PveBonusLoadoutBin",
"PvpBonusLoadoutBin",
"SentinelBin",
"SpaceSuitBin",
"SpaceWeaponBin",

View File

@ -63,22 +63,6 @@
}
],
"SyndicateMissions": [
{
"_id": { "$oid": "663a4fc5ba6f84724fa48049" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Tag": "ArbitersSyndicate",
"Seed": 24491,
"Nodes": ["SolNode223", "SolNode89", "SolNode146", "SolNode212", "SolNode167", "SolNode48", "SolNode78"]
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa4804a" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Tag": "CephalonSudaSyndicate",
"Seed": 12770,
"Nodes": ["SolNode36", "SolNode59", "SettlementNode12", "SolNode61", "SolNode12", "SolNode138", "SolNode72"]
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa4804c" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
@ -103,14 +87,6 @@
"Seed": 50102,
"Nodes": []
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa4804e" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Tag": "NewLokaSyndicate",
"Seed": 16064,
"Nodes": ["SolNode101", "SolNode224", "SolNode205", "SettlementNode2", "SolNode171", "SolNode188", "SolNode75"]
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa4804f" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
@ -119,14 +95,6 @@
"Seed": 77721,
"Nodes": []
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa48050" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Tag": "PerrinSyndicate",
"Seed": 9940,
"Nodes": ["SolNode39", "SolNode14", "SolNode203", "SolNode100", "SolNode130", "SolNode64", "SettlementNode15"]
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa48052" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
@ -255,14 +223,6 @@
"Seed": 67257,
"Nodes": []
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa4805e" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Tag": "RedVeilSyndicate",
"Seed": 46649,
"Nodes": ["SolNode226", "SolNode79", "SolNode216", "SettlementNode11", "SolNode56", "SolNode41", "SolNode23"]
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa48060" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
@ -270,14 +230,6 @@
"Tag": "VoxSyndicate",
"Seed": 77972,
"Nodes": []
},
{
"_id": { "$oid": "663a4fc5ba6f84724fa48061" },
"Activation": { "$date": { "$numberLong": "1715097541439" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Tag": "SteelMeridianSyndicate",
"Seed": 42366,
"Nodes": ["SolNode27", "SolNode107", "SolNode214", "SettlementNode1", "SolNode177", "SolNode141", "SolNode408"]
}
],
"ActiveMissions": [

View File

@ -85,6 +85,7 @@
<input class="form-control" type="password" id="password" required />
<br />
<button class="btn btn-primary" type="submit" data-loc="login_loginButton"></button>
<button class="btn btn-secondary" type="submit" onclick="registerSubmit = true;" data-loc="login_registerButton"></button>
</form>
</div>
<div data-route="/webui/inventory" data-title="Inventory | OpenWF WebUI">
@ -490,8 +491,9 @@
</div>
<div class="card mb-3">
<h5 class="card-header" data-loc="general_bulkActions"></h5>
<div class="card-body">
<div class="card-body d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="doAddAllMods();" data-loc="mods_bulkAddMods"></button>
<button class="btn btn-danger" onclick="doRemoveUnrankedMods();" data-loc="mods_removeUnranked"></button>
</div>
</div>
</div>

View File

@ -1,8 +1,15 @@
let loginOrRegisterPending = false;
window.registerSubmit = false;
function doLogin() {
if (loginOrRegisterPending) {
return;
}
loginOrRegisterPending = true;
localStorage.setItem("email", $("#email").val());
localStorage.setItem("password", $("#password").val());
$("#email, #password").val("");
loginFromLocalStorage();
registerSubmit = false;
}
function loginFromLocalStorage() {
@ -37,12 +44,15 @@ function doLoginRequest(succ_cb, fail_cb) {
s: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==", // signature of some kind
lang: "en",
date: 1501230947855458660, // ???
ClientType: "webui",
ClientType: registerSubmit ? "" : "webui",
PS: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==" // anti-cheat data
})
});
req.done(succ_cb);
req.fail(fail_cb);
req.always(() => {
loginOrRegisterPending = false;
});
}
function revalidateAuthz(succ_cb) {
@ -436,17 +446,35 @@ function updateInventory() {
category != "SpaceSuits" &&
category != "Sentinels" &&
category != "Hoverboards" &&
category != "MechSuits"
category != "MechSuits" &&
category != "MoaPets" &&
category != "KubrowPets"
) {
maxXP /= 2;
}
if (item.XP < maxXP) {
let anyExaltedMissingXP = false;
if (item.XP >= maxXP && "exalted" in itemMap[item.ItemType]) {
for (const exaltedType of itemMap[item.ItemType].exalted) {
const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType);
if (exaltedItem) {
const exaltedCap = itemMap[exaltedType]?.type == "weapons" ? 800_000 : 1_600_000;
if (exaltedItem.XP < exaltedCap) {
anyExaltedMissingXP = true;
break;
}
}
}
}
if (item.XP < maxXP || anyExaltedMissingXP) {
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
event.preventDefault();
if (item.XP < maxXP) {
addGearExp(category, item.ItemId.$oid, maxXP - item.XP);
}
if ("exalted" in itemMap[item.ItemType]) {
for (const exaltedType of itemMap[item.ItemType].exalted) {
const exaltedItem = data.SpecialItems.find(x => x.ItemType == exaltedType);
@ -786,7 +814,7 @@ function updateInventory() {
{
const td = document.createElement("td");
td.classList = "text-end text-nowrap";
{
if (maxRank != 0) {
const a = document.createElement("a");
a.href = "#";
a.onclick = function (event) {
@ -1594,6 +1622,31 @@ function doAddAllMods() {
});
}
function doRemoveUnrankedMods() {
revalidateAuthz(() => {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(inventory => {
window.itemListPromise.then(itemMap => {
$.post({
url: "/api/sell.php?" + window.authz,
contentType: "text/plain",
data: JSON.stringify({
SellCurrency: "SC_RegularCredits",
SellPrice: 0,
Items: {
Upgrades: inventory.RawUpgrades.filter(
x => !itemMap[x.ItemType]?.parazon && x.ItemCount > 0
).map(x => ({ String: x.ItemType, Count: x.ItemCount }))
}
})
}).done(function () {
updateInventory();
});
});
});
});
}
// Powersuit Route
single.getRoute("#powersuit-route").on("beforeload", function () {

View File

@ -54,12 +54,13 @@ dict = {
code_completed: `Abgeschlossen`,
code_active: `Aktiv`,
code_pigment: `Pigment`,
code_mature: `[UNTRANSLATED] Mature for combat`,
code_unmature: `[UNTRANSLATED] Regress genetic aging`,
code_mature: `Für den Kampf auswachsen lassen`,
code_unmature: `Genetisches Altern zurücksetzen`,
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_passwordLabel: `Passwort`,
login_loginButton: `Anmelden`,
login_registerButton: `[UNTRANSLATED] Register`,
navbar_logout: `Abmelden`,
navbar_renameAccount: `Account umbenennen`,
navbar_deleteAccount: `Account löschen`,
@ -82,7 +83,7 @@ dict = {
inventory_operatorAmps: `Verstärker`,
inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moa`,
inventory_kubrowPets: `[UNTRANSLATED] Beasts`,
inventory_kubrowPets: `Bestien`,
inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`,
inventory_bulkAddWeapons: `Fehlende Waffen hinzufügen`,
inventory_bulkAddSpaceSuits: `Fehlende Archwings hinzufügen`,
@ -115,6 +116,7 @@ dict = {
mods_rivens: `Rivens`,
mods_mods: `Mods`,
mods_bulkAddMods: `Fehlende Mods hinzufügen`,
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
cheats_administratorRequirement: `Du musst Administrator sein, um diese Funktion nutzen zu können. Um Administrator zu werden, füge <code>|DISPLAYNAME|</code> zu <code>administratorNames</code> in der config.json hinzu.`,
cheats_server: `Server`,
cheats_skipTutorial: `Tutorial überspringen`,

View File

@ -59,6 +59,7 @@ dict = {
login_emailLabel: `Email address`,
login_passwordLabel: `Password`,
login_loginButton: `Login`,
login_registerButton: `Register`,
navbar_logout: `Logout`,
navbar_renameAccount: `Rename Account`,
navbar_deleteAccount: `Delete Account`,
@ -114,6 +115,7 @@ dict = {
mods_rivens: `Rivens`,
mods_mods: `Mods`,
mods_bulkAddMods: `Add Missing Mods`,
mods_removeUnranked: `Remove Unranked Mods`,
cheats_administratorRequirement: `You must be an administrator to use this feature. To become an administrator, add <code>|DISPLAYNAME|</code> to <code>administratorNames</code> in the config.json.`,
cheats_server: `Server`,
cheats_skipTutorial: `Skip Tutorial`,

View File

@ -60,6 +60,7 @@ dict = {
login_emailLabel: `Dirección de correo electrónico`,
login_passwordLabel: `Contraseña`,
login_loginButton: `Iniciar sesión`,
login_registerButton: `[UNTRANSLATED] Register`,
navbar_logout: `Cerrar sesión`,
navbar_renameAccount: `Renombrar cuenta`,
navbar_deleteAccount: `Eliminar cuenta`,
@ -115,6 +116,7 @@ dict = {
mods_rivens: `Agrietados`,
mods_mods: `Mods`,
mods_bulkAddMods: `Agregar mods faltantes`,
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
cheats_administratorRequirement: `Debes ser administrador para usar esta función. Para convertirte en administrador, agrega <code>|DISPLAYNAME|</code> a <code>administratorNames</code> en el archivo config.json.`,
cheats_server: `Servidor`,
cheats_skipTutorial: `Omitir tutorial`,

View File

@ -25,10 +25,10 @@ dict = {
code_renamePrompt: `Nouveau nom :`,
code_remove: `Retirer`,
code_addItemsConfirm: `Ajouter |COUNT| items à l'inventaire ?`,
code_succRankUp: `[UNTRANSLATED] Successfully ranked up.`,
code_noEquipmentToRankUp: `No equipment to rank up.`,
code_succRankUp: `Montée de niveau effectuée.`,
code_noEquipmentToRankUp: `Aucun équipement à monter de niveau.`,
code_succAdded: `Ajouté.`,
code_succRemoved: `[UNTRANSLATED] Successfully removed.`,
code_succRemoved: `Retiré.`,
code_buffsNumber: `Nombre de buffs`,
code_cursesNumber: `Nombre de débuffs`,
code_rerollsNumber: `Nombre de rerolls`,
@ -45,21 +45,22 @@ dict = {
code_zanukaA: `Molosse Dorma`,
code_zanukaB: `Molosse Bhaira`,
code_zanukaC: `Molosse Hec`,
code_stage: `[UNTRANSLATED] Stage`,
code_complete: `[UNTRANSLATED] Complete`,
code_nextStage: `[UNTRANSLATED] Next stage`,
code_prevStage: `[UNTRANSLATED] Previous stage`,
code_reset: `[UNTRANSLATED] Reset`,
code_setInactive: `[UNTRANSLATED] Make the quest inactive`,
code_completed: `[UNTRANSLATED] Completed`,
code_active: `[UNTRANSLATED] Active`,
code_stage: `Étape`,
code_complete: `Compléter`,
code_nextStage: `Étape suivante`,
code_prevStage: `Étape précédente`,
code_reset: `Réinitialiser`,
code_setInactive: `Rendre la quête inactive`,
code_completed: `Complétée`,
code_active: `Active`,
code_pigment: `Pigment`,
code_mature: `[UNTRANSLATED] Mature for combat`,
code_unmature: `[UNTRANSLATED] Regress genetic aging`,
code_mature: `Maturer pour le combat`,
code_unmature: `Régrésser l'âge génétique`,
login_description: `Connexion avec les informations de connexion OpenWF.`,
login_emailLabel: `Email`,
login_passwordLabel: `Mot de passe`,
login_loginButton: `Connexion`,
login_registerButton: `[UNTRANSLATED] Register`,
navbar_logout: `Déconnexion`,
navbar_renameAccount: `Renommer le compte`,
navbar_deleteAccount: `Supprimer le compte`,
@ -82,7 +83,7 @@ dict = {
inventory_operatorAmps: `Amplificateurs`,
inventory_hoverboards: `K-Drives`,
inventory_moaPets: `Moa`,
inventory_kubrowPets: `[UNTRANSLATED] Beasts`,
inventory_kubrowPets: `Bêtes`,
inventory_bulkAddSuits: `Ajouter les Warframes manquantes`,
inventory_bulkAddWeapons: `Ajouter les armes manquantes`,
inventory_bulkAddSpaceSuits: `Ajouter les Archwings manquants`,
@ -107,14 +108,15 @@ dict = {
currency_PrimeTokens: `Aya Raffiné`,
currency_owned: `|COUNT| possédés.`,
powersuit_archonShardsLabel: `Emplacements de fragments d'Archonte`,
powersuit_archonShardsDescription: `Slots illimités pour appliquer plusieurs améliorations.`,
powersuit_archonShardsDescription2: `[UNTRANSLATED] Note that each archon shard takes some time to be applied when loading in.`,
powersuit_archonShardsDescription: `Slots illimités pour appliquer plusieurs améliorations`,
powersuit_archonShardsDescription2: `Un délai sera présent entre l'application des éclats et le chargement en jeu.`,
mods_addRiven: `Ajouter un riven`,
mods_fingerprint: `Empreinte`,
mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`,
mods_rivens: `Rivens`,
mods_mods: `Mods`,
mods_bulkAddMods: `Ajouter les mods manquants`,
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
cheats_administratorRequirement: `Rôle d'administrateur requis pour cette fonctionnalité. Ajoutez <code>|DISPLAYNAME|</code> à la ligne <code>administratorNames</code> dans le fichier config.json.`,
cheats_server: `Serveur`,
cheats_skipTutorial: `Passer le tutoriel`,
@ -131,32 +133,32 @@ dict = {
cheats_unlockAllFlavourItems: `Débloquer tous les <abbr title=\"Animations, Glyphes, Palettes, etc.\">Flavor Items</abbr>`,
cheats_unlockAllSkins: `Débloquer tous les skins`,
cheats_unlockAllCapturaScenes: `Débloquer toutes les scènes captura`,
cheats_unlockAllDecoRecipes: `[UNTRANSLATED] Unlock All Dojo Deco Recipes`,
cheats_unlockAllDecoRecipes: `Débloquer toutes les recherches dojo`,
cheats_universalPolarityEverywhere: `Polarités universelles partout`,
cheats_unlockDoubleCapacityPotatoesEverywhere: `Réacteurs et Catalyseurs partout`,
cheats_unlockExilusEverywhere: `Adaptateurs Exilus partout`,
cheats_unlockArcanesEverywhere: `Adaptateur d'Arcanes partout`,
cheats_noDailyStandingLimits: `Pas de limite de réputation journalière`,
cheats_noDailyFocusLimit: `[UNTRANSLATED] No Daily Focus Limits`,
cheats_noArgonCrystalDecay: `[UNTRANSLATED] No Argon Crystal Decay`,
cheats_noMasteryRankUpCooldown: `[UNTRANSLATED] No Mastery Rank Up Cooldown`,
cheats_noVendorPurchaseLimits: `[UNTRANSLATED] No Vendor Purchase Limits`,
cheats_noDeathMarks: `[UNTRANSLATED] No Death Marks`,
cheats_noKimCooldowns: `[UNTRANSLATED] No KIM Cooldowns`,
cheats_instantResourceExtractorDrones: `Ressources de drone d'extraction instantannées`,
cheats_noResourceExtractorDronesDamage: `[UNTRANSLATED] No Resource Extractor Drones Damage`,
cheats_noDojoRoomBuildStage: `No Dojo Room Build Stage`,
cheats_noDojoDecoBuildStage: `[UNTRANSLATED] No Dojo Deco Build Stage`,
cheats_fastDojoRoomDestruction: `[UNTRANSLATED] Fast Dojo Room Destruction`,
cheats_noDailyStandingLimits: `Aucune limite de réputation journalière`,
cheats_noDailyFocusLimit: `Aucune limite journalière de focus`,
cheats_noArgonCrystalDecay: `Aucune désintégration des Cristaux d'Argon`,
cheats_noMasteryRankUpCooldown: `Aucune attente pour la montée de rang de maîtrise`,
cheats_noVendorPurchaseLimits: `Aucune limite d'achat chez les PNJ`,
cheats_noDeathMarks: `Aucune marque d'assassin`,
cheats_noKimCooldowns: `Aucun cooldown sur le KIM`,
cheats_instantResourceExtractorDrones: `Ressources de drones d'extraction instantannées`,
cheats_noResourceExtractorDronesDamage: `Aucun dégâts aux drones d'extraction de resources`,
cheats_noDojoRoomBuildStage: `Aucune attente (construction des salles)`,
cheats_noDojoDecoBuildStage: `Aucune attente (construction des décorations)`,
cheats_fastDojoRoomDestruction: `Destruction de salle instantanée (Dojo)`,
cheats_noDojoResearchCosts: `Aucun coût de recherche (Dojo)`,
cheats_noDojoResearchTime: `Aucun temps de recherche (Dojo)`,
cheats_fastClanAscension: `[UNTRANSLATED] Fast Clan Ascension`,
cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`,
cheats_fastClanAscension: `Ascension de clan rapide`,
cheats_spoofMasteryRank: `Rang de maîtrise personnalisé (-1 pour désactiver)`,
cheats_saveSettings: `Sauvegarder les paramètres`,
cheats_account: `Compte`,
cheats_unlockAllFocusSchools: `Débloquer toutes les écoles de focus`,
cheats_helminthUnlockAll: `Helminth niveau max`,
cheats_intrinsicsUnlockAll: `[UNTRANSLATED] Max Rank All Intrinsics`,
cheats_intrinsicsUnlockAll: `Inhérences niveau max`,
cheats_changeSupportedSyndicate: `Allégeance`,
cheats_changeButton: `Changer`,
cheats_none: `Aucun`,

View File

@ -60,6 +60,7 @@ dict = {
login_emailLabel: `Адрес электронной почты`,
login_passwordLabel: `Пароль`,
login_loginButton: `Войти`,
login_registerButton: `[UNTRANSLATED] Register`,
navbar_logout: `Выйти`,
navbar_renameAccount: `Переименовать аккаунт`,
navbar_deleteAccount: `Удалить аккаунт`,
@ -108,13 +109,14 @@ dict = {
currency_owned: `У тебя |COUNT|.`,
powersuit_archonShardsLabel: `Ячейки осколков архонта`,
powersuit_archonShardsDescription: `Вы можете использовать эти неограниченные ячейки для установки множества улучшений.`,
powersuit_archonShardsDescription2: `[UNTRANSLATED] Note that each archon shard takes some time to be applied when loading in.`,
powersuit_archonShardsDescription2: `Обратите внимание: каждый фрагмент архонта применяется с задержкой при загрузке.`,
mods_addRiven: `Добавить Мод Разлома`,
mods_fingerprint: `Отпечаток`,
mods_fingerprintHelp: `Нужна помощь с отпечатком?`,
mods_rivens: `Моды Разлома`,
mods_mods: `Моды`,
mods_bulkAddMods: `Добавить отсутствующие моды`,
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
cheats_administratorRequirement: `Вы должны быть администратором для использования этой функции. Чтобы стать администратором, добавьте <code>\"|DISPLAYNAME|\"</code> в <code>administratorNames</code> в config.json.`,
cheats_server: `Сервер`,
cheats_skipTutorial: `Пропустить обучение`,
@ -136,15 +138,15 @@ dict = {
cheats_unlockDoubleCapacityPotatoesEverywhere: `Катализаторы везде`,
cheats_unlockExilusEverywhere: `Адаптеры Эксилус везде`,
cheats_unlockArcanesEverywhere: `Адаптеры для мистификаторов везде`,
cheats_noDailyStandingLimits: `Без ежедневных ограничений репутации`,
cheats_noDailyFocusLimit: `[UNTRANSLATED] No Daily Focus Limits`,
cheats_noDailyStandingLimits: `Без ежедневных лимитов репутации`,
cheats_noDailyFocusLimit: `Без ежедневных лимитов фокуса`,
cheats_noArgonCrystalDecay: `Без распада аргоновых кристаллов`,
cheats_noMasteryRankUpCooldown: `Повышение ранга мастерства без кулдауна`,
cheats_noVendorPurchaseLimits: `Отсутствие лимитов на покупки у вендоров`,
cheats_noDeathMarks: `[UNTRANSLATED] No Death Marks`,
cheats_noKimCooldowns: `[UNTRANSLATED] No KIM Cooldowns`,
cheats_noDeathMarks: `Без меток сметри`,
cheats_noKimCooldowns: `Чаты KIM без кулдауна`,
cheats_instantResourceExtractorDrones: `Мгновенные Экстракторы Ресурсов`,
cheats_noResourceExtractorDronesDamage: `[UNTRANSLATED] No Resource Extractor Drones Damage`,
cheats_noResourceExtractorDronesDamage: `Без урона по дронам-сборщикам`,
cheats_noDojoRoomBuildStage: `Мгновенное Строительтво Комнат Додзё`,
cheats_noDojoDecoBuildStage: `Мгновенное Строительтво Декораций Додзё`,
cheats_fastDojoRoomDestruction: `Мгновенные Уничтожение Комнат Додзё`,

View File

@ -60,6 +60,7 @@ dict = {
login_emailLabel: `电子邮箱`,
login_passwordLabel: `密码`,
login_loginButton: `登录`,
login_registerButton: `[UNTRANSLATED] Register`,
navbar_logout: `退出登录`,
navbar_renameAccount: `重命名账户`,
navbar_deleteAccount: `删除账户`,
@ -115,6 +116,7 @@ dict = {
mods_rivens: `裂罅MOD`,
mods_mods: `Mods`,
mods_bulkAddMods: `添加缺失MOD`,
mods_removeUnranked: `[UNTRANSLATED] Remove Unranked Mods`,
cheats_administratorRequirement: `您必须是管理员才能使用此功能。要成为管理员,请将 <code>|DISPLAYNAME|</code> 添加到 config.json 的 <code>administratorNames</code> 中。`,
cheats_server: `服务器`,
cheats_skipTutorial: `跳过教程`,