forked from OpenWF/SpaceNinjaServer
Compare commits
73 Commits
f796f9a851
...
4a2d863c9c
Author | SHA1 | Date | |
---|---|---|---|
4a2d863c9c | |||
9f0cd91105 | |||
ebfef52fb1 | |||
dd7bacd22e | |||
c00967931e | |||
b15a635e11 | |||
7e618539fa | |||
a29398fae6 | |||
601091f1c0 | |||
f561884f2c | |||
6e1cb0c9f9 | |||
9286627668 | |||
f94f2005d3 | |||
9901b7af54 | |||
2fa846f465 | |||
541ec3d702 | |||
0a28eab65d | |||
8e639a16bd | |||
522924a823 | |||
48e3f324e2 | |||
8f77c722cb | |||
e7287933b5 | |||
b21bca7a6d | |||
d30d450311 | |||
b62e326920 | |||
8b4bc114f6 | |||
564aa06762 | |||
2e84f71af8 | |||
ddfa98e0b2 | |||
bb3c3e01b0 | |||
695dcf98e0 | |||
509f7f0d9b | |||
aada031a80 | |||
a2a441ecb0 | |||
c0a0463a68 | |||
2307a40833 | |||
304af514e2 | |||
ddf3cd49b5 | |||
41e3f0136f | |||
c0ca9d9398 | |||
0f6b55beed | |||
f8550e9afe | |||
b53c4d9125 | |||
922b65cfab | |||
2f642df20a | |||
62314e89c7 | |||
56aa3e3331 | |||
c3f486488f | |||
49c353d895 | |||
90ab560620 | |||
b0e80fcfa8 | |||
2c62fb3c3c | |||
5b215733aa | |||
39866b9a2b | |||
fad1ee9314 | |||
64b43fcccf | |||
e407262cf8 | |||
00e57c43df | |||
2ab9f39507 | |||
b60723ef54 | |||
b3bf291d10 | |||
db86e2d265 | |||
f6cb8414c1 | |||
ba3df4bdbc | |||
8feb3a5b3c | |||
66f3d65d77 | |||
b18f06087b | |||
987b5b98ff | |||
fbbd9076cf | |||
838818543c | |||
a16e2716f1 | |||
f4c7ce582b | |||
c0187f9446 |
@ -21,7 +21,7 @@
|
||||
"@typescript-eslint/no-unsafe-argument": "error",
|
||||
"@typescript-eslint/no-unsafe-call": "error",
|
||||
"@typescript-eslint/no-unsafe-assignment": "error",
|
||||
"@typescript-eslint/no-explicit-any": "error",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"no-loss-of-precision": "error",
|
||||
"@typescript-eslint/no-unnecessary-condition": "error",
|
||||
"@typescript-eslint/no-base-to-string": "off",
|
||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
with:
|
||||
node-version: ">=20.6.0"
|
||||
- run: npm ci
|
||||
- run: cp config.json.example config.json
|
||||
- run: cp config-vanilla.json config.json
|
||||
- run: npm run verify
|
||||
- run: npm run lint:ci
|
||||
- run: npm run prettier
|
||||
|
@ -2,3 +2,4 @@ src/routes/api.ts
|
||||
static/webui/libs/
|
||||
*.html
|
||||
*.md
|
||||
config-vanilla.json
|
||||
|
@ -7,5 +7,6 @@ WORKDIR /app
|
||||
|
||||
RUN npm i --omit=dev
|
||||
RUN npm run build
|
||||
RUN date '+%d %B %Y' > BUILD_DATE
|
||||
|
||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||
|
@ -10,7 +10,7 @@ To get an idea of what functionality you can expect to be missing [have a look t
|
||||
|
||||
## config.json
|
||||
|
||||
SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [config.json.example](config.json.example), which has most cheats disabled.
|
||||
SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [config-vanilla.json](config-vanilla.json), which has most cheats disabled.
|
||||
|
||||
- `logger.level` can be `fatal`, `error`, `warn`, `info`, `http`, `debug`, or `trace`.
|
||||
- `myIrcAddresses` can be used to point to an IRC server. If not provided, defaults to `[ myAddress ]`.
|
||||
|
@ -70,8 +70,9 @@
|
||||
"creditBoost": false,
|
||||
"affinityBoost": false,
|
||||
"resourceBoost": false,
|
||||
"starDays": true,
|
||||
"tennoLiveRelay": false,
|
||||
"galleonOfGhouls": 0,
|
||||
"starDaysOverride": null,
|
||||
"eidolonOverride": "",
|
||||
"vallisOverride": "",
|
||||
"duviriOverride": "",
|
@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
if [ ! -f conf/config.json ]; then
|
||||
jq --arg value "mongodb://openwfagent:spaceninjaserver@mongodb:27017/" '.mongodbUrl = $value' /app/config.json.example > /app/conf/config.json
|
||||
jq --arg value "mongodb://openwfagent:spaceninjaserver@mongodb:27017/" '.mongodbUrl = $value' /app/config-vanilla.json > /app/conf/config.json
|
||||
fi
|
||||
|
||||
exec npm run start -- --configPath conf/config.json
|
||||
|
660
package-lock.json
generated
660
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@
|
||||
"dev": "node scripts/dev.js",
|
||||
"dev:bun": "bun scripts/dev.js",
|
||||
"verify": "tsgo --noEmit",
|
||||
"verify:tsc": "tsc --noEmit",
|
||||
"bun-run": "bun src/index.ts",
|
||||
"lint": "eslint --ext .ts .",
|
||||
"lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
|
||||
@ -39,7 +40,7 @@
|
||||
"ncp": "^2.0.0",
|
||||
"typescript": "^5.5",
|
||||
"undici": "^7.10.0",
|
||||
"warframe-public-export-plus": "^0.5.78",
|
||||
"warframe-public-export-plus": "^0.5.79",
|
||||
"warframe-riven-info": "^0.1.2",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
|
22
src/controllers/api/apartmentController.ts
Normal file
22
src/controllers/api/apartmentController.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const apartmentController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const personalRooms = await getPersonalRooms(accountId, "Apartment");
|
||||
const response: IApartmentResponse = {};
|
||||
if (req.query.backdrop !== undefined) {
|
||||
response.NewBackdropItem = personalRooms.Apartment.VideoWallBackdrop = req.query.backdrop as string;
|
||||
}
|
||||
if (req.query.soundscape !== undefined) {
|
||||
response.NewSoundscapeItem = personalRooms.Apartment.Soundscape = req.query.soundscape as string;
|
||||
}
|
||||
await personalRooms.save();
|
||||
res.json(response);
|
||||
};
|
||||
|
||||
interface IApartmentResponse {
|
||||
NewBackdropItem?: string;
|
||||
NewSoundscapeItem?: string;
|
||||
}
|
@ -4,8 +4,7 @@ import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inven
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { createUnveiledRivenFingerprint } from "@/src/helpers/rivenHelper";
|
||||
import { ExportUpgrades } from "warframe-public-export-plus";
|
||||
import { IVeiledRivenFingerprint } from "@/src/helpers/rivenHelper";
|
||||
|
||||
export const completeRandomModChallengeController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -27,10 +26,11 @@ export const completeRandomModChallengeController: RequestHandler = async (req,
|
||||
inventoryChanges.MiscItems = miscItemChanges;
|
||||
}
|
||||
|
||||
// Update riven fingerprint to a randomised unveiled state
|
||||
// Complete the riven challenge
|
||||
const upgrade = inventory.Upgrades.id(request.ItemId)!;
|
||||
const meta = ExportUpgrades[upgrade.ItemType];
|
||||
upgrade.UpgradeFingerprint = JSON.stringify(createUnveiledRivenFingerprint(meta));
|
||||
const fp = JSON.parse(upgrade.UpgradeFingerprint!) as IVeiledRivenFingerprint;
|
||||
fp.challenge.Progress = fp.challenge.Required;
|
||||
upgrade.UpgradeFingerprint = JSON.stringify(fp);
|
||||
|
||||
await inventory.save();
|
||||
|
||||
|
@ -88,7 +88,6 @@ export const crewShipFusionController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
}
|
||||
superiorItem.UpgradeFingerprint = JSON.stringify(fingerprint);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
inventoryChanges[category] = [superiorItem.toJSON() as any];
|
||||
|
||||
await inventory.save();
|
||||
|
@ -3,11 +3,13 @@ import {
|
||||
getGuildForRequestEx,
|
||||
hasAccessToDojo,
|
||||
hasGuildPermission,
|
||||
refundDojoDeco,
|
||||
removeDojoDeco
|
||||
} from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const destroyDojoDecoController: RequestHandler = async (req, res) => {
|
||||
@ -18,9 +20,20 @@ export const destroyDojoDecoController: RequestHandler = async (req, res) => {
|
||||
res.json({ DojoRequestStatus: -1 });
|
||||
return;
|
||||
}
|
||||
const request = JSON.parse(String(req.body)) as IDestroyDojoDecoRequest;
|
||||
|
||||
removeDojoDeco(guild, request.ComponentId, request.DecoId);
|
||||
const request = JSON.parse(String(req.body)) as IDestroyDojoDecoRequest | IClearObstacleCourseRequest;
|
||||
if ("DecoType" in request) {
|
||||
removeDojoDeco(guild, request.ComponentId, request.DecoId);
|
||||
} else if (request.Act == "cObst") {
|
||||
const component = guild.DojoComponents.id(request.ComponentId)!;
|
||||
if (component.Decos) {
|
||||
for (const deco of component.Decos) {
|
||||
refundDojoDeco(guild, component, deco);
|
||||
}
|
||||
component.Decos.splice(0, component.Decos.length);
|
||||
}
|
||||
} else {
|
||||
logger.error(`unhandled destroyDojoDeco request`, request);
|
||||
}
|
||||
|
||||
await guild.save();
|
||||
res.json(await getDojoClient(guild, 0, request.ComponentId));
|
||||
@ -31,3 +44,8 @@ interface IDestroyDojoDecoRequest {
|
||||
ComponentId: string;
|
||||
DecoId: string;
|
||||
}
|
||||
|
||||
interface IClearObstacleCourseRequest {
|
||||
ComponentId: string;
|
||||
Act: "cObst" | "maybesomethingelsewedontknowabout";
|
||||
}
|
||||
|
@ -2,22 +2,14 @@ import { RequestHandler } from "express";
|
||||
import { ExportResources } from "warframe-public-export-plus";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addFusionTreasures, addMiscItems, getInventory } from "@/src/services/inventoryService";
|
||||
import { IFusionTreasure, IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { parseFusionTreasure } from "@/src/helpers/inventoryHelpers";
|
||||
|
||||
interface IFusionTreasureRequest {
|
||||
oldTreasureName: string;
|
||||
newTreasureName: string;
|
||||
}
|
||||
|
||||
const parseFusionTreasure = (name: string, count: number): IFusionTreasure => {
|
||||
const arr = name.split("_");
|
||||
return {
|
||||
ItemType: arr[0],
|
||||
Sockets: parseInt(arr[1], 16),
|
||||
ItemCount: count
|
||||
};
|
||||
};
|
||||
|
||||
export const fusionTreasuresController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
@ -73,4 +74,5 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||
InventoryChanges: inventoryChanges,
|
||||
AffiliationMods: affiliationMods
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
};
|
||||
|
@ -335,6 +335,17 @@ export const getInventoryResponse = async (
|
||||
for (const uniqueName in ExportFlavour) {
|
||||
inventoryResponse.FlavourItems.push({ ItemType: uniqueName });
|
||||
}
|
||||
} else if (config.worldState?.baroTennoConRelay) {
|
||||
[
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2022EarlyAccess",
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2023EarlyAccess",
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2024EarlyAccess",
|
||||
"/Lotus/Types/Items/Events/TennoConRelay2025EarlyAccess"
|
||||
].forEach(uniqueName => {
|
||||
if (!inventoryResponse.FlavourItems.some(x => x.ItemType == uniqueName)) {
|
||||
inventoryResponse.FlavourItems.push({ ItemType: uniqueName });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (config.unlockAllSkins) {
|
||||
|
@ -130,7 +130,7 @@ const createLoginResponse = (
|
||||
resp.Groups = [];
|
||||
}
|
||||
if (version_compare(buildLabel, "2021.04.13.19.58") >= 0) {
|
||||
resp.DTLS = 99;
|
||||
resp.DTLS = 0; // bit 0 enables DTLS. if enabled, additional bits can be set, e.g. bit 2 to enable logging. on live, the value is 99.
|
||||
}
|
||||
if (version_compare(buildLabel, "2022.04.29.12.53") >= 0) {
|
||||
resp.ClientType = account.ClientType;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import {
|
||||
getInventory,
|
||||
@ -194,4 +195,5 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
MiscItems: miscItemChanges
|
||||
}
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
} from "@/src/services/inventoryService";
|
||||
import { getDefaultUpgrades } from "@/src/services/itemDataService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { EquipmentFeatures } from "@/src/types/equipmentTypes";
|
||||
@ -68,6 +69,7 @@ export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
} else {
|
||||
throw new Error(`unknown modularWeaponSale op: ${String(req.query.op)}`);
|
||||
}
|
||||
|
@ -16,15 +16,28 @@ export const playerSkillsController: RequestHandler = async (req, res) => {
|
||||
inventory.PlayerSkills[request.Skill as keyof IPlayerSkills]++;
|
||||
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
if (request.Skill == "LPS_COMMAND" && inventory.PlayerSkills.LPS_COMMAND == 9) {
|
||||
const consumablesChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Restoratives/Consumable/CrewmateBall",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addConsumables(inventory, consumablesChanges);
|
||||
inventoryChanges.Consumables = consumablesChanges;
|
||||
if (request.Skill == "LPS_COMMAND") {
|
||||
if (inventory.PlayerSkills.LPS_COMMAND == 9) {
|
||||
const consumablesChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Restoratives/Consumable/CrewmateBall",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addConsumables(inventory, consumablesChanges);
|
||||
inventoryChanges.Consumables = consumablesChanges;
|
||||
}
|
||||
} else if (request.Skill == "LPS_DRIFT_RIDING") {
|
||||
if (inventory.PlayerSkills.LPS_DRIFT_RIDING == 9) {
|
||||
const consumablesChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Restoratives/ErsatzSummon",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addConsumables(inventory, consumablesChanges);
|
||||
inventoryChanges.Consumables = consumablesChanges;
|
||||
}
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
|
@ -11,7 +11,7 @@ export const projectionManagerController: RequestHandler = async (req, res) => {
|
||||
const [era, category, currentQuality] = parseProjection(request.projectionType);
|
||||
const upgradeCost = config.dontSubtractVoidTraces
|
||||
? 0
|
||||
: (request.qualityTag - qualityKeywordToNumber[currentQuality]) * 25;
|
||||
: qualityNumberToCost[request.qualityTag] - qualityNumberToCost[qualityKeywordToNumber[currentQuality]];
|
||||
const newProjectionType = findProjection(era, category, qualityNumberToKeyword[request.qualityTag]);
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
@ -49,6 +49,7 @@ const qualityKeywordToNumber: Record<VoidProjectionQuality, number> = {
|
||||
VPQ_GOLD: 2,
|
||||
VPQ_PLATINUM: 3
|
||||
};
|
||||
const qualityNumberToCost = [0, 25, 50, 100];
|
||||
|
||||
// e.g. "/Lotus/Types/Game/Projections/T2VoidProjectionProteaPrimeDBronze" -> ["Lith", "W5", "VPQ_BRONZE"]
|
||||
const parseProjection = (typeName: string): [string, string, VoidProjectionQuality] => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const releasePetController: RequestHandler = async (req, res) => {
|
||||
@ -19,6 +20,7 @@ export const releasePetController: RequestHandler = async (req, res) => {
|
||||
|
||||
await inventory.save();
|
||||
res.json({ inventoryChanges }); // Not a mistake; it's "inventoryChanges" here.
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
};
|
||||
|
||||
interface IReleasePetRequest {
|
||||
|
@ -9,13 +9,16 @@ import {
|
||||
freeUpSlot,
|
||||
combineInventoryChanges,
|
||||
addCrewShipRawSalvage,
|
||||
addFusionPoints
|
||||
addFusionPoints,
|
||||
addCrewShipFusionPoints,
|
||||
addFusionTreasures
|
||||
} from "@/src/services/inventoryService";
|
||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
import { sendWsBroadcastEx } from "@/src/services/wsService";
|
||||
import { parseFusionTreasure } from "@/src/helpers/inventoryHelpers";
|
||||
|
||||
export const sellController: RequestHandler = async (req, res) => {
|
||||
const payload = JSON.parse(String(req.body)) as ISellRequest;
|
||||
@ -26,6 +29,8 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
requiredFields.add("RegularCredits");
|
||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||
requiredFields.add("FusionPoints");
|
||||
} else if (payload.SellCurrency == "SC_CrewShipFusionPoints") {
|
||||
requiredFields.add("CrewShipFusionPoints");
|
||||
} else {
|
||||
requiredFields.add("MiscItems");
|
||||
}
|
||||
@ -79,6 +84,8 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
inventory.RegularCredits += payload.SellPrice;
|
||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||
addFusionPoints(inventory, payload.SellPrice);
|
||||
} else if (payload.SellCurrency == "SC_CrewShipFusionPoints") {
|
||||
addCrewShipFusionPoints(inventory, payload.SellPrice);
|
||||
} else if (payload.SellCurrency == "SC_PrimeBucks") {
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
@ -290,12 +297,17 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
]);
|
||||
});
|
||||
}
|
||||
if (payload.Items.FusionTreasures) {
|
||||
payload.Items.FusionTreasures.forEach(sellItem => {
|
||||
addFusionTreasures(inventory, [parseFusionTreasure(sellItem.String, sellItem.Count * -1)]);
|
||||
});
|
||||
}
|
||||
|
||||
await inventory.save();
|
||||
res.json({
|
||||
inventoryChanges: inventoryChanges // "inventoryChanges" for this response instead of the usual "InventoryChanges"
|
||||
});
|
||||
sendWsBroadcastTo(accountId, { update_inventory: true });
|
||||
sendWsBroadcastEx({ update_inventory: true }, accountId, parseInt(String(req.query.wsid)));
|
||||
};
|
||||
|
||||
interface ISellRequest {
|
||||
@ -322,6 +334,7 @@ interface ISellRequest {
|
||||
CrewMembers?: ISellItem[];
|
||||
CrewShipWeapons?: ISellItem[];
|
||||
CrewShipWeaponSkins?: ISellItem[];
|
||||
FusionTreasures?: ISellItem[];
|
||||
};
|
||||
SellPrice: number;
|
||||
SellCurrency:
|
||||
@ -330,7 +343,8 @@ interface ISellRequest {
|
||||
| "SC_FusionPoints"
|
||||
| "SC_DistillPoints"
|
||||
| "SC_CrewShipFusionPoints"
|
||||
| "SC_Resources";
|
||||
| "SC_Resources"
|
||||
| "somethingelsewemightnotknowabout";
|
||||
buildLabel: string;
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,17 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IShipDecorationsRequest } from "@/src/types/personalRoomsTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { IShipDecorationsRequest, IResetShipDecorationsRequest } from "@/src/types/personalRoomsTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { handleSetShipDecorations } from "@/src/services/shipCustomizationsService";
|
||||
import { handleResetShipDecorations, handleSetShipDecorations } from "@/src/services/shipCustomizationsService";
|
||||
|
||||
export const shipDecorationsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const shipDecorationsRequest = JSON.parse(req.body as string) as IShipDecorationsRequest;
|
||||
|
||||
try {
|
||||
if (req.query.reset == "1") {
|
||||
const request = JSON.parse(req.body as string) as IResetShipDecorationsRequest;
|
||||
const response = await handleResetShipDecorations(accountId, request);
|
||||
res.send(response);
|
||||
} else {
|
||||
const shipDecorationsRequest = JSON.parse(req.body as string) as IShipDecorationsRequest;
|
||||
const placedDecoration = await handleSetShipDecorations(accountId, shipDecorationsRequest);
|
||||
res.send(placedDecoration);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
logger.error(`error in shipDecorationsController: ${error.message}`);
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ import { logger } from "@/src/utils/logger";
|
||||
export const updateChallengeProgressController: RequestHandler = async (req, res) => {
|
||||
const challenges = getJSONfromString<IUpdateChallengeProgressRequest>(String(req.body));
|
||||
const account = await getAccountForRequest(req);
|
||||
logger.debug(`challenge report:`, challenges);
|
||||
|
||||
const inventory = await getInventory(
|
||||
account._id.toString(),
|
||||
@ -17,7 +18,7 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
|
||||
);
|
||||
let affiliationMods: IAffiliationMods[] = [];
|
||||
if (challenges.ChallengeProgress) {
|
||||
affiliationMods = addChallenges(
|
||||
affiliationMods = await addChallenges(
|
||||
account,
|
||||
inventory,
|
||||
challenges.ChallengeProgress,
|
||||
|
33
src/controllers/custom/abilityOverrideController.ts
Normal file
33
src/controllers/custom/abilityOverrideController.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const abilityOverrideController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = req.body as IAbilityOverrideRequest;
|
||||
if (request.category === "Suits") {
|
||||
const inventory = await getInventory(accountId, request.category);
|
||||
const item = inventory[request.category].id(request.oid);
|
||||
if (item) {
|
||||
if (request.action == "set") {
|
||||
item.Configs[request.configIndex].AbilityOverride = request.AbilityOverride;
|
||||
} else {
|
||||
item.Configs[request.configIndex].AbilityOverride = undefined;
|
||||
}
|
||||
await inventory.save();
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IAbilityOverrideRequest {
|
||||
category: TEquipmentKey;
|
||||
oid: string;
|
||||
action: "set" | "remove";
|
||||
configIndex: number;
|
||||
AbilityOverride: {
|
||||
Ability: string;
|
||||
Index: number;
|
||||
};
|
||||
}
|
65
src/controllers/custom/changeModularPartsController.ts
Normal file
65
src/controllers/custom/changeModularPartsController.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const changeModularPartsController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const request = req.body as IUpdateFingerPrintRequest;
|
||||
const inventory = await getInventory(accountId, request.category);
|
||||
const item = inventory[request.category].id(request.oid);
|
||||
if (item) {
|
||||
item.ModularParts = request.modularParts;
|
||||
|
||||
request.modularParts.forEach(part => {
|
||||
const categoryMap = mapping[part];
|
||||
if (categoryMap && categoryMap[request.category]) {
|
||||
item.ItemType = categoryMap[request.category]!;
|
||||
}
|
||||
});
|
||||
await inventory.save();
|
||||
}
|
||||
res.end();
|
||||
};
|
||||
|
||||
interface IUpdateFingerPrintRequest {
|
||||
category: TEquipmentKey;
|
||||
oid: string;
|
||||
modularParts: string[];
|
||||
}
|
||||
|
||||
const mapping: Partial<Record<string, Partial<Record<TEquipmentKey, string>>>> = {
|
||||
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelAPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun"
|
||||
},
|
||||
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelEgg/InfModularBarrelEggPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun"
|
||||
},
|
||||
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelBPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary"
|
||||
},
|
||||
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelCPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary"
|
||||
},
|
||||
"/Lotus/Weapons/SolarisUnited/Secondary/SUModularSecondarySet1/Barrel/SUModularSecondaryBarrelDPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
||||
},
|
||||
"/Lotus/Weapons/Infested/Pistols/InfKitGun/Barrels/InfBarrelBeam/InfModularBarrelBeamPart": {
|
||||
LongGuns: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam",
|
||||
Pistols: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
||||
},
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA": {
|
||||
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit"
|
||||
},
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB": {
|
||||
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit"
|
||||
},
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC": {
|
||||
MoaPets: "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
|
||||
}
|
||||
};
|
@ -1,8 +1,8 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { config } from "@/src/services/configService";
|
||||
import { config, syncConfigWithDatabase } from "@/src/services/configService";
|
||||
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
|
||||
import { saveConfig } from "@/src/services/configWriterService";
|
||||
import { sendWsBroadcastExcept } from "@/src/services/wsService";
|
||||
import { sendWsBroadcastEx } from "@/src/services/wsService";
|
||||
|
||||
export const getConfigController: RequestHandler = async (req, res) => {
|
||||
const account = await getAccountForRequest(req);
|
||||
@ -25,7 +25,8 @@ export const setConfigController: RequestHandler = async (req, res) => {
|
||||
const [obj, idx] = configIdToIndexable(id);
|
||||
obj[idx] = value;
|
||||
}
|
||||
sendWsBroadcastExcept(parseInt(String(req.query.wsid)), { config_reloaded: true });
|
||||
sendWsBroadcastEx({ config_reloaded: true }, undefined, parseInt(String(req.query.wsid)));
|
||||
syncConfigWithDatabase();
|
||||
await saveConfig();
|
||||
res.end();
|
||||
} else {
|
||||
|
@ -11,6 +11,7 @@ import { GuildMember } from "@/src/models/guildModel";
|
||||
import { Leaderboard } from "@/src/models/leaderboardModel";
|
||||
import { deleteGuild } from "@/src/services/guildService";
|
||||
import { Friendship } from "@/src/models/friendModel";
|
||||
import { sendWsBroadcastTo } from "@/src/services/wsService";
|
||||
|
||||
export const deleteAccountController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -36,5 +37,8 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
|
||||
Ship.deleteMany({ ShipOwnerId: accountId }),
|
||||
Stats.deleteOne({ accountOwnerId: accountId })
|
||||
]);
|
||||
|
||||
sendWsBroadcastTo(accountId, { logged_out: true });
|
||||
|
||||
res.end();
|
||||
};
|
||||
|
@ -0,0 +1,34 @@
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
const DEFAULT_UPGRADE_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||
|
||||
export const editSuitInvigorationUpgradeController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const { oid, data } = req.body as {
|
||||
oid: string;
|
||||
data?: {
|
||||
DefensiveUpgrade: string;
|
||||
OffensiveUpgrade: string;
|
||||
UpgradesExpiry?: number;
|
||||
};
|
||||
};
|
||||
const inventory = await getInventory(accountId);
|
||||
const suit = inventory.Suits.id(oid)!;
|
||||
if (data) {
|
||||
suit.DefensiveUpgrade = data.DefensiveUpgrade;
|
||||
suit.OffensiveUpgrade = data.OffensiveUpgrade;
|
||||
if (data.UpgradesExpiry) {
|
||||
suit.UpgradesExpiry = new Date(data.UpgradesExpiry);
|
||||
} else {
|
||||
suit.UpgradesExpiry = new Date(Date.now() + DEFAULT_UPGRADE_EXPIRY_MS);
|
||||
}
|
||||
} else {
|
||||
suit.DefensiveUpgrade = undefined;
|
||||
suit.OffensiveUpgrade = undefined;
|
||||
suit.UpgradesExpiry = undefined;
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getDict, getItemName, getString } from "@/src/services/itemDataService";
|
||||
import {
|
||||
ExportAbilities,
|
||||
ExportArcanes,
|
||||
ExportAvionics,
|
||||
ExportBoosters,
|
||||
@ -57,6 +58,7 @@ interface ItemLists {
|
||||
mods: ListedItem[];
|
||||
Boosters: ListedItem[];
|
||||
VarziaOffers: ListedItem[];
|
||||
Abilities: ListedItem[];
|
||||
//circuitGameModes: ListedItem[];
|
||||
}
|
||||
|
||||
@ -94,7 +96,8 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
EvolutionProgress: [],
|
||||
mods: [],
|
||||
Boosters: [],
|
||||
VarziaOffers: []
|
||||
VarziaOffers: [],
|
||||
Abilities: []
|
||||
/*circuitGameModes: [
|
||||
{
|
||||
uniqueName: "Survival",
|
||||
@ -132,6 +135,12 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
name: getString(item.name, lang),
|
||||
exalted: item.exalted
|
||||
});
|
||||
item.abilities.forEach(ability => {
|
||||
res.Abilities.push({
|
||||
uniqueName: ability.uniqueName,
|
||||
name: getString(ability.name || uniqueName, lang)
|
||||
});
|
||||
});
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
|
||||
if (item.productCategory == "Sentinels" || item.productCategory == "KubrowPets") {
|
||||
@ -144,18 +153,21 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
|
||||
if (item.partType) {
|
||||
if (!uniqueName.startsWith("/Lotus/Types/Items/Deimos/")) {
|
||||
res.ModularParts.push({
|
||||
uniqueName,
|
||||
name: getString(item.name, lang),
|
||||
partType: item.partType
|
||||
});
|
||||
}
|
||||
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
|
||||
res.miscitems.push({
|
||||
uniqueName: uniqueName,
|
||||
name: getString(item.name, lang)
|
||||
});
|
||||
if (!uniqueName.split("/")[7]?.startsWith("PvPVariant")) {
|
||||
// not a pvp variant
|
||||
if (!uniqueName.startsWith("/Lotus/Types/Items/Deimos/")) {
|
||||
res.ModularParts.push({
|
||||
uniqueName,
|
||||
name: getString(item.name, lang),
|
||||
partType: item.partType
|
||||
});
|
||||
}
|
||||
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
|
||||
res.miscitems.push({
|
||||
uniqueName: uniqueName,
|
||||
name: getString(item.name, lang)
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (item.totalDamage !== 0) {
|
||||
if (
|
||||
@ -348,6 +360,13 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
});
|
||||
}
|
||||
|
||||
for (const [uniqueName, ability] of Object.entries(ExportAbilities)) {
|
||||
res.Abilities.push({
|
||||
uniqueName,
|
||||
name: getString(ability.name || uniqueName, lang)
|
||||
});
|
||||
}
|
||||
|
||||
response.json(res);
|
||||
};
|
||||
|
||||
|
@ -141,7 +141,7 @@ export const getProfileViewingDataGetController: RequestHandler = async (req, re
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
combinedStats[arrayName].push(entry as any);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { IMongoDate, IOid, IOidWithLegacySupport } from "@/src/types/commonTypes";
|
||||
import { Types } from "mongoose";
|
||||
import { TRarity } from "warframe-public-export-plus";
|
||||
import { IFusionTreasure } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
|
||||
export const version_compare = (a: string, b: string): number => {
|
||||
const a_digits = a
|
||||
@ -51,6 +52,15 @@ export const fromMongoDate = (date: IMongoDate): Date => {
|
||||
return new Date(parseInt(date.$date.$numberLong));
|
||||
};
|
||||
|
||||
export const parseFusionTreasure = (name: string, count: number): IFusionTreasure => {
|
||||
const arr = name.split("_");
|
||||
return {
|
||||
ItemType: arr[0],
|
||||
Sockets: parseInt(arr[1], 16),
|
||||
ItemCount: count
|
||||
};
|
||||
};
|
||||
|
||||
export type TTraitsPool = Record<
|
||||
"Colors" | "EyeColors" | "FurPatterns" | "BodyTypes" | "Heads" | "Tails",
|
||||
{ type: string; rarity: TRarity }[]
|
||||
|
18
src/index.ts
18
src/index.ts
@ -1,5 +1,5 @@
|
||||
// First, init config.
|
||||
import { config, configPath, loadConfig } from "@/src/services/configService";
|
||||
import { config, configPath, loadConfig, syncConfigWithDatabase } from "@/src/services/configService";
|
||||
import fs from "fs";
|
||||
try {
|
||||
loadConfig();
|
||||
@ -7,7 +7,7 @@ try {
|
||||
if (fs.existsSync("config.json")) {
|
||||
console.log("Failed to load " + configPath + ": " + (e as Error).message);
|
||||
} else {
|
||||
console.log("Failed to load " + configPath + ". You can copy config.json.example to create your config file.");
|
||||
console.log("Failed to load " + configPath + ". You can copy config-vanilla.json to create your config file.");
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
@ -18,17 +18,23 @@ logger.info("Starting up...");
|
||||
|
||||
// Proceed with normal startup: bring up config watcher service, validate config, connect to MongoDB, and finally start listening for HTTP.
|
||||
import mongoose from "mongoose";
|
||||
import path from "path";
|
||||
import { JSONStringify } from "json-with-bigint";
|
||||
import { startWebServer } from "@/src/services/webService";
|
||||
|
||||
import { syncConfigWithDatabase, validateConfig } from "@/src/services/configWatcherService";
|
||||
import { validateConfig } from "@/src/services/configWatcherService";
|
||||
import { updateWorldStateCollections } from "@/src/services/worldStateService";
|
||||
import { repoDir } from "@/src/helpers/pathHelper";
|
||||
|
||||
// Patch JSON.stringify to work flawlessly with Bigints.
|
||||
JSON.stringify = JSONStringify;
|
||||
JSON.stringify = JSONStringify; // Patch JSON.stringify to work flawlessly with Bigints.
|
||||
|
||||
validateConfig();
|
||||
|
||||
fs.readFile(path.join(repoDir, "BUILD_DATE"), "utf-8", (err, data) => {
|
||||
if (!err) {
|
||||
logger.info(`Docker image was built on ${data.trim()}`);
|
||||
}
|
||||
});
|
||||
|
||||
mongoose
|
||||
.connect(config.mongodbUrl)
|
||||
.then(() => {
|
||||
|
@ -1,16 +1,11 @@
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { logError } from "@/src/utils/logger";
|
||||
|
||||
export const errorHandler = (err: Error, req: Request, res: Response, _next: NextFunction): void => {
|
||||
if (err.message == "Invalid accountId-nonce pair") {
|
||||
res.status(400).send("Log-in expired");
|
||||
} else if (err.stack) {
|
||||
const stackArr = err.stack.split("\n");
|
||||
stackArr[0] += ` while processing ${req.path} request`;
|
||||
logger.error(stackArr.join("\n"));
|
||||
res.status(500).end();
|
||||
} else {
|
||||
logger.error(`uncaught error while processing ${req.path} request: ${err.message}`);
|
||||
logError(err, `processing ${req.path} request`);
|
||||
res.status(500).end();
|
||||
}
|
||||
};
|
||||
|
@ -4,8 +4,12 @@ import { typeCountSchema } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IMongoDate, IOid, ITypeCount } from "@/src/types/commonTypes";
|
||||
|
||||
export interface IMessageClient
|
||||
extends Omit<IMessageDatabase, "_id" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly" | "expiry"> {
|
||||
extends Omit<
|
||||
IMessageDatabase,
|
||||
"_id" | "globaUpgradeId" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly" | "expiry"
|
||||
> {
|
||||
_id?: IOid;
|
||||
globaUpgradeId?: IOid; // [sic]
|
||||
date: IMongoDate;
|
||||
startDate?: IMongoDate;
|
||||
endDate?: IMongoDate;
|
||||
@ -14,6 +18,7 @@ export interface IMessageClient
|
||||
|
||||
export interface IMessageDatabase extends IMessage {
|
||||
ownerId: Types.ObjectId;
|
||||
globaUpgradeId?: Types.ObjectId; // [sic]
|
||||
date: Date; //created at
|
||||
attVisualOnly?: boolean;
|
||||
_id: Types.ObjectId;
|
||||
@ -101,6 +106,7 @@ const giftSchema = new Schema<IGift>(
|
||||
const messageSchema = new Schema<IMessageDatabase>(
|
||||
{
|
||||
ownerId: Schema.Types.ObjectId,
|
||||
globaUpgradeId: Schema.Types.ObjectId,
|
||||
sndr: String,
|
||||
msg: String,
|
||||
cinematic: String,
|
||||
@ -144,7 +150,7 @@ messageSchema.virtual("messageId").get(function (this: IMessageDatabase) {
|
||||
|
||||
messageSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
const messageDatabase = returnedObject as IMessageDatabase;
|
||||
const messageClient = returnedObject as IMessageClient;
|
||||
|
||||
@ -154,6 +160,10 @@ messageSchema.set("toJSON", {
|
||||
delete returnedObject.attVisualOnly;
|
||||
delete returnedObject.expiry;
|
||||
|
||||
if (messageDatabase.globaUpgradeId) {
|
||||
messageClient.globaUpgradeId = toOid(messageDatabase.globaUpgradeId);
|
||||
}
|
||||
|
||||
messageClient.date = toMongoDate(messageDatabase.date);
|
||||
|
||||
if (messageDatabase.startDate && messageDatabase.endDate) {
|
||||
|
@ -121,7 +121,7 @@ import {
|
||||
export const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
|
||||
|
||||
typeCountSchema.set("toJSON", {
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
if (obj.ItemCount > 2147483647) {
|
||||
obj.ItemCount = 2147483647;
|
||||
} else if (obj.ItemCount < -2147483648) {
|
||||
@ -189,7 +189,7 @@ operatorConfigSchema.virtual("ItemId").get(function () {
|
||||
|
||||
operatorConfigSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -226,7 +226,7 @@ const ItemConfigSchema = new Schema<IItemConfig>(
|
||||
);
|
||||
|
||||
ItemConfigSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
});
|
||||
@ -261,7 +261,7 @@ RawUpgrades.virtual("LastAdded").get(function () {
|
||||
|
||||
RawUpgrades.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -282,7 +282,7 @@ upgradeSchema.virtual("ItemId").get(function () {
|
||||
|
||||
upgradeSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -325,7 +325,7 @@ const crewMemberSchema = new Schema<ICrewMemberDatabase>(
|
||||
|
||||
crewMemberSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as ICrewMemberDatabase;
|
||||
const client = obj as ICrewMemberClient;
|
||||
|
||||
@ -353,7 +353,7 @@ const FlavourItemSchema = new Schema(
|
||||
);
|
||||
|
||||
FlavourItemSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -367,7 +367,7 @@ FlavourItemSchema.set("toJSON", {
|
||||
);
|
||||
|
||||
MailboxSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
const mailboxDatabase = returnedObject as HydratedDocument<IMailboxDatabase, { __v?: number }>;
|
||||
delete mailboxDatabase.__v;
|
||||
(returnedObject as IMailboxClient).LastInboxId = toOid(mailboxDatabase.LastInboxId);
|
||||
@ -386,7 +386,7 @@ const DuviriInfoSchema = new Schema<IDuviriInfo>(
|
||||
);
|
||||
|
||||
DuviriInfoSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
});
|
||||
@ -416,7 +416,7 @@ const droneSchema = new Schema<IDroneDatabase>(
|
||||
);
|
||||
droneSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, obj) {
|
||||
transform(_document, obj: Record<string, any>) {
|
||||
const client = obj as IDroneClient;
|
||||
const db = obj as IDroneDatabase;
|
||||
|
||||
@ -457,7 +457,7 @@ const personalGoalProgressSchema = new Schema<IPersonalGoalProgressDatabase>(
|
||||
|
||||
personalGoalProgressSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IPersonalGoalProgressDatabase;
|
||||
const client = obj as IPersonalGoalProgressClient;
|
||||
|
||||
@ -502,7 +502,7 @@ StepSequencersSchema.virtual("ItemId").get(function () {
|
||||
|
||||
StepSequencersSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
}
|
||||
@ -516,7 +516,7 @@ const kubrowPetEggSchema = new Schema<IKubrowPetEggDatabase>(
|
||||
);
|
||||
kubrowPetEggSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, obj) {
|
||||
transform(_document, obj: Record<string, any>) {
|
||||
const client = obj as IKubrowPetEggClient;
|
||||
const db = obj as IKubrowPetEggDatabase;
|
||||
|
||||
@ -586,7 +586,7 @@ personalTechProjectSchema.virtual("ItemId").get(function () {
|
||||
|
||||
personalTechProjectSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
|
||||
@ -687,7 +687,7 @@ const questKeysSchema = new Schema<IQuestKeyDatabase>(
|
||||
);
|
||||
|
||||
questKeysSchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
const questKeysDatabase = ret as IQuestKeyDatabase;
|
||||
|
||||
if (questKeysDatabase.CompletionDate) {
|
||||
@ -709,7 +709,7 @@ const invasionProgressSchema = new Schema<IInvasionProgressDatabase>(
|
||||
);
|
||||
|
||||
invasionProgressSchema.set("toJSON", {
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IInvasionProgressDatabase;
|
||||
const client = obj as IInvasionProgressClient;
|
||||
|
||||
@ -748,7 +748,7 @@ weaponSkinsSchema.virtual("ItemId").get(function () {
|
||||
|
||||
weaponSkinsSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
}
|
||||
@ -772,7 +772,7 @@ const periodicMissionCompletionsSchema = new Schema<IPeriodicMissionCompletionDa
|
||||
);
|
||||
|
||||
periodicMissionCompletionsSchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
const periodicMissionCompletionDatabase = ret as IPeriodicMissionCompletionDatabase;
|
||||
|
||||
(periodicMissionCompletionDatabase as unknown as IPeriodicMissionCompletionResponse).date = toMongoDate(
|
||||
@ -849,7 +849,7 @@ const endlessXpProgressSchema = new Schema<IEndlessXpProgressDatabase>(
|
||||
);
|
||||
|
||||
endlessXpProgressSchema.set("toJSON", {
|
||||
transform(_doc, ret) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
const db = ret as IEndlessXpProgressDatabase;
|
||||
const client = ret as IEndlessXpProgressClient;
|
||||
|
||||
@ -898,7 +898,7 @@ const crewShipMemberSchema = new Schema<ICrewShipMemberDatabase>(
|
||||
);
|
||||
crewShipMemberSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as ICrewShipMemberDatabase;
|
||||
const client = obj as ICrewShipMemberClient;
|
||||
if (db.ItemId) {
|
||||
@ -951,7 +951,7 @@ const dialogueSchema = new Schema<IDialogueDatabase>(
|
||||
);
|
||||
dialogueSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
const db = ret as IDialogueDatabase;
|
||||
const client = ret as IDialogueClient;
|
||||
|
||||
@ -997,7 +997,7 @@ const kubrowPetPrintSchema = new Schema<IKubrowPetPrintDatabase>({
|
||||
});
|
||||
kubrowPetPrintSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IKubrowPetPrintDatabase;
|
||||
const client = obj as IKubrowPetPrintClient;
|
||||
|
||||
@ -1025,7 +1025,7 @@ const detailsSchema = new Schema<IKubrowPetDetailsDatabase>(
|
||||
);
|
||||
|
||||
detailsSchema.set("toJSON", {
|
||||
transform(_doc, returnedObject) {
|
||||
transform(_doc, returnedObject: Record<string, any>) {
|
||||
delete returnedObject.__v;
|
||||
|
||||
const db = returnedObject as IKubrowPetDetailsDatabase;
|
||||
@ -1081,7 +1081,7 @@ EquipmentSchema.virtual("ItemId").get(function () {
|
||||
|
||||
EquipmentSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
|
||||
@ -1132,7 +1132,7 @@ pendingRecipeSchema.virtual("ItemId").get(function () {
|
||||
|
||||
pendingRecipeSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.LongGuns;
|
||||
@ -1170,7 +1170,7 @@ const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
|
||||
);
|
||||
|
||||
infestedFoundrySchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
if (ret.AbilityOverrideUnlockCooldown) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
ret.AbilityOverrideUnlockCooldown = toMongoDate(ret.AbilityOverrideUnlockCooldown);
|
||||
@ -1243,7 +1243,7 @@ const vendorPurchaseHistoryEntrySchema = new Schema<IVendorPurchaseHistoryEntryD
|
||||
);
|
||||
|
||||
vendorPurchaseHistoryEntrySchema.set("toJSON", {
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IVendorPurchaseHistoryEntryDatabase;
|
||||
const client = obj as IVendorPurchaseHistoryEntryClient;
|
||||
client.Expiry = toMongoDate(db.Expiry);
|
||||
@ -1286,7 +1286,7 @@ const pendingCouponSchema = new Schema<IPendingCouponDatabase>(
|
||||
);
|
||||
|
||||
pendingCouponSchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
(ret as IPendingCouponClient).Expiry = toMongoDate((ret as IPendingCouponDatabase).Expiry);
|
||||
}
|
||||
});
|
||||
@ -1353,7 +1353,7 @@ const nemesisSchema = new Schema<INemesisDatabase>(
|
||||
|
||||
nemesisSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as INemesisDatabase;
|
||||
const client = obj as INemesisClient;
|
||||
|
||||
@ -1383,7 +1383,7 @@ const lastSortieRewardSchema = new Schema<ILastSortieRewardDatabase>(
|
||||
|
||||
lastSortieRewardSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as ILastSortieRewardDatabase;
|
||||
const client = obj as ILastSortieRewardClient;
|
||||
|
||||
@ -1437,6 +1437,8 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
PremiumCreditsFree: { type: Number, default: 0 },
|
||||
//Endo
|
||||
FusionPoints: { type: Number, default: 0 },
|
||||
//Dirac
|
||||
CrewShipFusionPoints: { type: Number, default: 0 },
|
||||
//Regal Aya
|
||||
PrimeTokens: { type: Number, default: 0 },
|
||||
|
||||
@ -1790,7 +1792,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
);
|
||||
|
||||
inventorySchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.accountOwnerId;
|
||||
|
@ -49,7 +49,7 @@ loadoutConfigSchema.virtual("ItemId").get(function () {
|
||||
|
||||
loadoutConfigSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
}
|
||||
@ -62,6 +62,7 @@ export const loadoutSchema = new Schema<ILoadoutDatabase, loadoutModelType>({
|
||||
NORMAL_PVP: [loadoutConfigSchema],
|
||||
LUNARO: [loadoutConfigSchema],
|
||||
OPERATOR: [loadoutConfigSchema],
|
||||
GEAR: [loadoutConfigSchema],
|
||||
KDRIVE: [loadoutConfigSchema],
|
||||
DATAKNIFE: [loadoutConfigSchema],
|
||||
MECH: [loadoutConfigSchema],
|
||||
@ -71,7 +72,7 @@ export const loadoutSchema = new Schema<ILoadoutDatabase, loadoutModelType>({
|
||||
});
|
||||
|
||||
loadoutSchema.set("toJSON", {
|
||||
transform(_doc, ret, _options) {
|
||||
transform(_doc, ret: Record<string, any>) {
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
delete ret.loadoutOwnerId;
|
||||
@ -88,6 +89,7 @@ type loadoutDocumentProps = {
|
||||
NORMAL_PVP: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
LUNARO: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
OPERATOR: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
GEAR: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
KDRIVE: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
DATAKNIFE: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
MECH: Types.DocumentArray<ILoadoutConfigDatabase>;
|
||||
|
@ -32,7 +32,7 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
|
||||
);
|
||||
|
||||
databaseAccountSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
},
|
||||
|
@ -55,7 +55,7 @@ placedDecosSchema.virtual("id").get(function (this: IPlacedDecosDatabase) {
|
||||
|
||||
placedDecosSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
}
|
||||
});
|
||||
@ -78,7 +78,7 @@ const favouriteLoadoutSchema = new Schema<IFavouriteLoadoutDatabase>(
|
||||
);
|
||||
favouriteLoadoutSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
returnedObject.LoadoutId = toOid(returnedObject.LoadoutId);
|
||||
}
|
||||
@ -95,7 +95,7 @@ const plantSchema = new Schema<IPlantDatabase>(
|
||||
|
||||
plantSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const client = obj as IPlantClient;
|
||||
const db = obj as IPlantDatabase;
|
||||
|
||||
@ -122,7 +122,9 @@ const apartmentSchema = new Schema<IApartmentDatabase>(
|
||||
{
|
||||
Rooms: [roomSchema],
|
||||
FavouriteLoadouts: [favouriteLoadoutSchema],
|
||||
Gardening: gardeningSchema
|
||||
Gardening: gardeningSchema,
|
||||
VideoWallBackdrop: String,
|
||||
Soundscape: String
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
@ -156,7 +158,7 @@ const orbiterSchema = new Schema<IOrbiterDatabase>(
|
||||
);
|
||||
orbiterSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_doc, obj) {
|
||||
transform(_doc, obj: Record<string, any>) {
|
||||
const db = obj as IOrbiterDatabase;
|
||||
const client = obj as IOrbiterClient;
|
||||
|
||||
|
@ -22,7 +22,7 @@ shipSchema.virtual("ItemId").get(function () {
|
||||
|
||||
shipSchema.set("toJSON", {
|
||||
virtuals: true,
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
const shipResponse = returnedObject as IShipInventory;
|
||||
const shipDatabase = returnedObject as IShipDatabase;
|
||||
delete returnedObject._id;
|
||||
|
@ -101,7 +101,7 @@ const statsSchema = new Schema<IStatsDatabase>({
|
||||
});
|
||||
|
||||
statsSchema.set("toJSON", {
|
||||
transform(_document, returnedObject) {
|
||||
transform(_document, returnedObject: Record<string, any>) {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.accountOwnerId;
|
||||
|
@ -10,6 +10,7 @@ import { addPendingFriendController } from "@/src/controllers/api/addPendingFrie
|
||||
import { addToAllianceController } from "@/src/controllers/api/addToAllianceController";
|
||||
import { addToGuildController } from "@/src/controllers/api/addToGuildController";
|
||||
import { adoptPetController } from "@/src/controllers/api/adoptPetController";
|
||||
import { apartmentController } from "@/src/controllers/api/apartmentController";
|
||||
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
|
||||
import { archonFusionController } from "@/src/controllers/api/archonFusionController";
|
||||
import { artifactsController } from "@/src/controllers/api/artifactsController";
|
||||
@ -168,6 +169,7 @@ const apiRouter = express.Router();
|
||||
// get
|
||||
apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController);
|
||||
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
|
||||
apiRouter.get("/apartment.php", apartmentController);
|
||||
apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementController);
|
||||
apiRouter.get("/changeDojoRoot.php", changeDojoRootController);
|
||||
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
|
||||
|
@ -15,6 +15,7 @@ import { webuiFileChangeDetectedController } from "@/src/controllers/custom/webu
|
||||
import { completeAllMissionsController } from "@/src/controllers/custom/completeAllMissionsController";
|
||||
import { addMissingHelminthBlueprintsController } from "@/src/controllers/custom/addMissingHelminthBlueprintsController";
|
||||
|
||||
import { abilityOverrideController } from "@/src/controllers/custom/abilityOverrideController";
|
||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
|
||||
import { createMessageController } from "@/src/controllers/custom/createMessageController";
|
||||
import { addCurrencyController } from "@/src/controllers/custom/addCurrencyController";
|
||||
@ -25,6 +26,8 @@ import { manageQuestsController } from "@/src/controllers/custom/manageQuestsCon
|
||||
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
|
||||
import { setBoosterController } from "@/src/controllers/custom/setBoosterController";
|
||||
import { updateFingerprintController } from "@/src/controllers/custom/updateFingerprintController";
|
||||
import { changeModularPartsController } from "@/src/controllers/custom/changeModularPartsController";
|
||||
import { editSuitInvigorationUpgradeController } from "@/src/controllers/custom/editSuitInvigorationUpgradeController";
|
||||
|
||||
import { getConfigController, setConfigController } from "@/src/controllers/custom/configController";
|
||||
|
||||
@ -45,6 +48,7 @@ customRouter.get("/webuiFileChangeDetected", webuiFileChangeDetectedController);
|
||||
customRouter.get("/completeAllMissions", completeAllMissionsController);
|
||||
customRouter.get("/addMissingHelminthBlueprints", addMissingHelminthBlueprintsController);
|
||||
|
||||
customRouter.post("/abilityOverride", abilityOverrideController);
|
||||
customRouter.post("/createAccount", createAccountController);
|
||||
customRouter.post("/createMessage", createMessageController);
|
||||
customRouter.post("/addCurrency", addCurrencyController);
|
||||
@ -55,6 +59,8 @@ customRouter.post("/manageQuests", manageQuestsController);
|
||||
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
|
||||
customRouter.post("/setBooster", setBoosterController);
|
||||
customRouter.post("/updateFingerprint", updateFingerprintController);
|
||||
customRouter.post("/changeModularParts", changeModularPartsController);
|
||||
customRouter.post("/editSuitInvigorationUpgrade", editSuitInvigorationUpgradeController);
|
||||
|
||||
customRouter.post("/getConfig", getConfigController);
|
||||
customRouter.post("/setConfig", setConfigController);
|
||||
|
@ -2,6 +2,7 @@ import fs from "fs";
|
||||
import path from "path";
|
||||
import { repoDir } from "@/src/helpers/pathHelper";
|
||||
import { args } from "@/src/helpers/commandLineArguments";
|
||||
import { Inbox } from "@/src/models/inboxModel";
|
||||
|
||||
export interface IConfig {
|
||||
mongodbUrl: string;
|
||||
@ -80,8 +81,10 @@ export interface IConfig {
|
||||
creditBoost?: boolean;
|
||||
affinityBoost?: boolean;
|
||||
resourceBoost?: boolean;
|
||||
starDays?: boolean;
|
||||
tennoLiveRelay?: boolean;
|
||||
baroTennoConRelay?: boolean;
|
||||
galleonOfGhouls?: number;
|
||||
starDaysOverride?: boolean;
|
||||
eidolonOverride?: string;
|
||||
vallisOverride?: string;
|
||||
duviriOverride?: string;
|
||||
@ -113,9 +116,26 @@ export const loadConfig = (): void => {
|
||||
|
||||
// Set all values to undefined now so if the new config.json omits some fields that were previously present, it's correct in-memory.
|
||||
for (const key of Object.keys(config)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
(config as any)[key] = undefined;
|
||||
}
|
||||
|
||||
Object.assign(config, newConfig);
|
||||
};
|
||||
|
||||
export const syncConfigWithDatabase = (): void => {
|
||||
// Event messages are deleted after endDate. Since we don't use beginDate/endDate and instead have config toggles, we need to delete the messages once those bools are false.
|
||||
// Also, for some reason, I can't just do `Inbox.deleteMany(...)`; - it needs this whole circus.
|
||||
if (!config.worldState?.creditBoost) {
|
||||
void Inbox.deleteMany({ globaUpgradeId: "5b23106f283a555109666672" }).then(() => {});
|
||||
}
|
||||
if (!config.worldState?.affinityBoost) {
|
||||
void Inbox.deleteMany({ globaUpgradeId: "5b23106f283a555109666673" }).then(() => {});
|
||||
}
|
||||
if (!config.worldState?.resourceBoost) {
|
||||
void Inbox.deleteMany({ globaUpgradeId: "5b23106f283a555109666674" }).then(() => {});
|
||||
}
|
||||
if (!config.worldState?.galleonOfGhouls) {
|
||||
void Inbox.deleteMany({ goalTag: "GalleonRobbery" }).then(() => {});
|
||||
}
|
||||
};
|
||||
|
@ -1,10 +1,9 @@
|
||||
import chokidar from "chokidar";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { config, configPath, loadConfig } from "@/src/services/configService";
|
||||
import { config, configPath, loadConfig, syncConfigWithDatabase } from "@/src/services/configService";
|
||||
import { saveConfig, shouldReloadConfig } from "@/src/services/configWriterService";
|
||||
import { getWebPorts, startWebServer, stopWebServer } from "@/src/services/webService";
|
||||
import { sendWsBroadcast } from "@/src/services/wsService";
|
||||
import { Inbox } from "@/src/models/inboxModel";
|
||||
import varzia from "@/static/fixed_responses/worldState/varzia.json";
|
||||
|
||||
chokidar.watch(configPath).on("change", () => {
|
||||
@ -68,10 +67,3 @@ export const validateConfig = (): void => {
|
||||
void saveConfig();
|
||||
}
|
||||
};
|
||||
|
||||
export const syncConfigWithDatabase = (): void => {
|
||||
// Event messages are deleted after endDate. Since we don't use beginDate/endDate and instead have config toggles, we need to delete the messages once those bools are false.
|
||||
if (!config.worldState?.galleonOfGhouls) {
|
||||
void Inbox.deleteMany({ goalTag: "GalleonRobbery" }).then(() => {}); // For some reason, I can't just do `Inbox.deleteMany(...)`; it needs this whole circus.
|
||||
}
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
IDojoComponentDatabase,
|
||||
IDojoContributable,
|
||||
IDojoDecoClient,
|
||||
IDojoDecoDatabase,
|
||||
IGuildClient,
|
||||
IGuildMemberClient,
|
||||
IGuildMemberDatabase,
|
||||
@ -309,7 +310,7 @@ export const removeDojoRoom = async (
|
||||
guild.DojoEnergy -= meta.energy;
|
||||
}
|
||||
moveResourcesToVault(guild, component);
|
||||
component.Decos?.forEach(deco => moveResourcesToVault(guild, deco));
|
||||
component.Decos?.forEach(deco => refundDojoDeco(guild, component, deco));
|
||||
|
||||
if (guild.RoomChanges) {
|
||||
const index = guild.RoomChanges.findIndex(x => x.componentId.equals(component._id));
|
||||
@ -344,6 +345,14 @@ export const removeDojoDeco = (
|
||||
component.Decos!.findIndex(x => x._id.equals(decoId)),
|
||||
1
|
||||
)[0];
|
||||
refundDojoDeco(guild, component, deco);
|
||||
};
|
||||
|
||||
export const refundDojoDeco = (
|
||||
guild: TGuildDatabaseDocument,
|
||||
component: IDojoComponentDatabase,
|
||||
deco: IDojoDecoDatabase
|
||||
): void => {
|
||||
const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type);
|
||||
if (meta) {
|
||||
if (meta.capacityCost) {
|
||||
@ -369,7 +378,7 @@ export const removeDojoDeco = (
|
||||
]);
|
||||
}
|
||||
}
|
||||
moveResourcesToVault(guild, deco);
|
||||
moveResourcesToVault(guild, deco); // Refund resources spent on construction
|
||||
};
|
||||
|
||||
const moveResourcesToVault = (guild: TGuildDatabaseDocument, component: IDojoContributable): void => {
|
||||
|
@ -422,6 +422,7 @@ export const importLoadOutPresets = (db: ILoadoutDatabase, client: ILoadOutPrese
|
||||
db.NORMAL_PVP = client.NORMAL_PVP.map(convertLoadOutConfig);
|
||||
db.LUNARO = client.LUNARO.map(convertLoadOutConfig);
|
||||
db.OPERATOR = client.OPERATOR.map(convertLoadOutConfig);
|
||||
db.GEAR = client.GEAR.map(convertLoadOutConfig);
|
||||
db.KDRIVE = client.KDRIVE.map(convertLoadOutConfig);
|
||||
db.DATAKNIFE = client.DATAKNIFE.map(convertLoadOutConfig);
|
||||
db.MECH = client.MECH.map(convertLoadOutConfig);
|
||||
|
@ -35,7 +35,7 @@ export const createNewEventMessages = async (req: Request): Promise<void> => {
|
||||
const baroIndex = Math.trunc((Date.now() - 910800000) / (unixTimesInMs.day * 14));
|
||||
const baroStart = baroIndex * (unixTimesInMs.day * 14) + 910800000;
|
||||
const baroActualStart = baroStart + unixTimesInMs.day * (config.baroAlwaysAvailable ? 0 : 12);
|
||||
if (account.LatestEventMessageDate.getTime() < baroActualStart) {
|
||||
if (Date.now() >= baroActualStart && account.LatestEventMessageDate.getTime() < baroActualStart) {
|
||||
newEventMessages.push({
|
||||
sndr: "/Lotus/Language/G1Quests/VoidTraderName",
|
||||
sub: "/Lotus/Language/CommunityMessages/VoidTraderAppearanceTitle",
|
||||
@ -55,20 +55,77 @@ export const createNewEventMessages = async (req: Request): Promise<void> => {
|
||||
}
|
||||
|
||||
// BUG: Deleting the inbox message manually means it'll just be automatically re-created. This is because we don't use startDate/endDate for these config-toggled events.
|
||||
if (config.worldState?.galleonOfGhouls) {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, goalTag: "GalleonRobbery" }))) {
|
||||
newEventMessages.push({
|
||||
sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
|
||||
sub: "/Lotus/Language/Events/GalleonRobberyIntroMsgTitle",
|
||||
msg: "/Lotus/Language/Events/GalleonRobberyIntroMsgDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png",
|
||||
transmission: "/Lotus/Sounds/Dialog/GalleonOfGhouls/DGhoulsWeekOneInbox0010VayHek",
|
||||
att: ["/Lotus/Upgrades/Skins/Events/OgrisOldSchool"],
|
||||
startDate: new Date(),
|
||||
goalTag: "GalleonRobbery"
|
||||
});
|
||||
}
|
||||
const promises = [];
|
||||
if (config.worldState?.creditBoost) {
|
||||
promises.push(
|
||||
(async (): Promise<void> => {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666672" }))) {
|
||||
newEventMessages.push({
|
||||
globaUpgradeId: new Types.ObjectId("5b23106f283a555109666672"),
|
||||
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
|
||||
sub: "/Lotus/Language/Items/EventDoubleCreditsName",
|
||||
msg: "/Lotus/Language/Items/EventDoubleCreditsDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
|
||||
startDate: new Date(),
|
||||
CrossPlatform: true
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
if (config.worldState?.affinityBoost) {
|
||||
promises.push(
|
||||
(async (): Promise<void> => {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666673" }))) {
|
||||
newEventMessages.push({
|
||||
globaUpgradeId: new Types.ObjectId("5b23106f283a555109666673"),
|
||||
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
|
||||
sub: "/Lotus/Language/Items/EventDoubleAffinityName",
|
||||
msg: "/Lotus/Language/Items/EventDoubleAffinityDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
|
||||
startDate: new Date(),
|
||||
CrossPlatform: true
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
if (config.worldState?.resourceBoost) {
|
||||
promises.push(
|
||||
(async (): Promise<void> => {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, globaUpgradeId: "5b23106f283a555109666674" }))) {
|
||||
newEventMessages.push({
|
||||
globaUpgradeId: new Types.ObjectId("5b23106f283a555109666674"),
|
||||
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
|
||||
sub: "/Lotus/Language/Items/EventDoubleResourceName",
|
||||
msg: "/Lotus/Language/Items/EventDoubleResourceDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Lotus_d.png",
|
||||
startDate: new Date(),
|
||||
CrossPlatform: true
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
if (config.worldState?.galleonOfGhouls) {
|
||||
promises.push(
|
||||
(async (): Promise<void> => {
|
||||
if (!(await Inbox.exists({ ownerId: account._id, goalTag: "GalleonRobbery" }))) {
|
||||
newEventMessages.push({
|
||||
sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
|
||||
sub: "/Lotus/Language/Events/GalleonRobberyIntroMsgTitle",
|
||||
msg: "/Lotus/Language/Events/GalleonRobberyIntroMsgDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png",
|
||||
transmission: "/Lotus/Sounds/Dialog/GalleonOfGhouls/DGhoulsWeekOneInbox0010VayHek",
|
||||
att: ["/Lotus/Upgrades/Skins/Events/OgrisOldSchool"],
|
||||
startDate: new Date(),
|
||||
goalTag: "GalleonRobbery"
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
if (newEventMessages.length === 0) {
|
||||
return;
|
||||
|
@ -25,7 +25,8 @@ import {
|
||||
INemesisWeaponTargetFingerprint,
|
||||
INemesisPetTargetFingerprint,
|
||||
IDialogueDatabase,
|
||||
IKubrowPetPrintClient
|
||||
IKubrowPetPrintClient,
|
||||
equipmentKeys
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "@/src/types/genericUpdate";
|
||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
||||
@ -1241,6 +1242,15 @@ export const addFusionPoints = (inventory: TInventoryDatabaseDocument, add: numb
|
||||
return add;
|
||||
};
|
||||
|
||||
export const addCrewShipFusionPoints = (inventory: TInventoryDatabaseDocument, add: number): number => {
|
||||
if (inventory.CrewShipFusionPoints + add > 2147483647) {
|
||||
logger.warn(`capping CrewShipFusionPoints balance at 2147483647`);
|
||||
add = 2147483647 - inventory.CrewShipFusionPoints;
|
||||
}
|
||||
inventory.CrewShipFusionPoints += add;
|
||||
return add;
|
||||
};
|
||||
|
||||
const standingLimitBinToInventoryKey: Record<
|
||||
Exclude<TStandingLimitBin, "STANDING_LIMIT_BIN_NONE">,
|
||||
keyof IDailyAffiliations
|
||||
@ -1332,7 +1342,7 @@ export const addStanding = (
|
||||
|
||||
// TODO: AffiliationMods support (Nightwave).
|
||||
export const updateGeneric = async (data: IGenericUpdate, accountId: string): Promise<IUpdateNodeIntrosResponse> => {
|
||||
const inventory = await getInventory(accountId, "NodeIntrosCompleted MiscItems");
|
||||
const inventory = await getInventory(accountId, "NodeIntrosCompleted MiscItems ShipDecorations");
|
||||
|
||||
// Make it an array for easier parsing.
|
||||
if (typeof data.NodeIntrosCompleted === "string") {
|
||||
@ -1341,7 +1351,15 @@ export const updateGeneric = async (data: IGenericUpdate, accountId: string): Pr
|
||||
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
for (const node of data.NodeIntrosCompleted) {
|
||||
if (node == "KayaFirstVisitPack") {
|
||||
if (node == "TC2025") {
|
||||
inventoryChanges.ShipDecorations = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Items/ShipDecos/TauGrineerLancerBobbleHead",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addShipDecorations(inventory, inventoryChanges.ShipDecorations);
|
||||
} else if (node == "KayaFirstVisitPack") {
|
||||
inventoryChanges.MiscItems = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Items/MiscItems/1999FixedStickersPack",
|
||||
@ -1662,28 +1680,34 @@ export const applyClientEquipmentUpdates = (
|
||||
item.XP ??= 0;
|
||||
item.XP += XP;
|
||||
|
||||
let xpItemType = item.ItemType;
|
||||
if (item.ModularParts) {
|
||||
for (const part of item.ModularParts) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const partType = ExportWeapons[part]?.partType;
|
||||
if (partType !== undefined && xpEarningParts.indexOf(partType) != -1) {
|
||||
xpItemType = part;
|
||||
break;
|
||||
if (
|
||||
categoryName != "SpecialItems" ||
|
||||
item.ItemType == "/Lotus/Powersuits/Khora/Kavat/KhoraKavatPowerSuit" ||
|
||||
item.ItemType == "/Lotus/Powersuits/Khora/Kavat/KhoraPrimeKavatPowerSuit"
|
||||
) {
|
||||
let xpItemType = item.ItemType;
|
||||
if (item.ModularParts) {
|
||||
for (const part of item.ModularParts) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const partType = ExportWeapons[part]?.partType;
|
||||
if (partType !== undefined && xpEarningParts.indexOf(partType) != -1) {
|
||||
xpItemType = part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
logger.debug(`adding xp to ${xpItemType} for modular item ${fromOid(ItemId)} (${item.ItemType})`);
|
||||
}
|
||||
logger.debug(`adding xp to ${xpItemType} for modular item ${fromOid(ItemId)} (${item.ItemType})`);
|
||||
}
|
||||
|
||||
const xpinfoIndex = inventory.XPInfo.findIndex(x => x.ItemType == xpItemType);
|
||||
if (xpinfoIndex !== -1) {
|
||||
const xpinfo = inventory.XPInfo[xpinfoIndex];
|
||||
xpinfo.XP += XP;
|
||||
} else {
|
||||
inventory.XPInfo.push({
|
||||
ItemType: xpItemType,
|
||||
XP: XP
|
||||
});
|
||||
const xpinfoIndex = inventory.XPInfo.findIndex(x => x.ItemType == xpItemType);
|
||||
if (xpinfoIndex !== -1) {
|
||||
const xpinfo = inventory.XPInfo[xpinfoIndex];
|
||||
xpinfo.XP += XP;
|
||||
} else {
|
||||
inventory.XPInfo.push({
|
||||
ItemType: xpItemType,
|
||||
XP: XP
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1888,25 +1912,87 @@ export const addLoreFragmentScans = (inventory: TInventoryDatabaseDocument, arr:
|
||||
});
|
||||
};
|
||||
|
||||
export const addChallenges = (
|
||||
const challengeRewardsInboxMessages: Record<string, IMessageCreationTemplate> = {
|
||||
SentEvoEphemeraRankOne: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingEphemeraUnlockAName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingEphemeraUnlockADesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Effects/NarmerEvolvingEphemeraB"]
|
||||
},
|
||||
SentEvoEphemeraRankTwo: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingEphemeraUnlockBName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingEphemeraUnlockBDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Effects/NarmerEvolvingEphemeraC"]
|
||||
},
|
||||
SentEvoSyandanaRankOne: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingSyandanaUnlockAName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingSyandanaUnlockADesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Scarves/NarmerEvolvingSyandanaBCape"]
|
||||
},
|
||||
SentEvoSyandanaRankTwo: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingSyandanaUnlockBName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingSyandanaUnlockBDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Scarves/NarmerEvolvingSyandanaCCape"]
|
||||
},
|
||||
SentEvoSekharaRankOne: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingSekharaUnlockAName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingSekharaUnlockADesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Clan/ZarimanEvolvingSekharaBadgeItemB"]
|
||||
},
|
||||
SentEvoSekharaRankTwo: {
|
||||
sub: "/Lotus/Language/Inbox/EvolvingSekharaUnlockBName",
|
||||
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||
msg: "/Lotus/Language/Inbox/EvolvingSekharaUnlockBDesc",
|
||||
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||
att: ["/Lotus/Upgrades/Skins/Clan/ZarimanEvolvingSekharaBadgeItemC"]
|
||||
}
|
||||
};
|
||||
|
||||
export const addChallenges = async (
|
||||
account: TAccountDocument,
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
ChallengeProgress: IChallengeProgress[],
|
||||
SeasonChallengeCompletions: ISeasonChallenge[] | undefined
|
||||
): IAffiliationMods[] => {
|
||||
ChallengeProgress.forEach(({ Name, Progress }) => {
|
||||
const itemIndex = inventory.ChallengeProgress.findIndex(i => i.Name === Name);
|
||||
|
||||
if (itemIndex !== -1) {
|
||||
inventory.ChallengeProgress[itemIndex].Progress = Progress;
|
||||
): Promise<IAffiliationMods[]> => {
|
||||
for (const { Name, Progress, Completed } of ChallengeProgress) {
|
||||
let dbChallenge = inventory.ChallengeProgress.find(x => x.Name == Name);
|
||||
if (dbChallenge) {
|
||||
dbChallenge.Progress = Progress;
|
||||
} else {
|
||||
inventory.ChallengeProgress.push({ Name, Progress });
|
||||
dbChallenge = { Name, Progress };
|
||||
inventory.ChallengeProgress.push(dbChallenge);
|
||||
}
|
||||
|
||||
if (Name.startsWith("Calendar")) {
|
||||
addString(getCalendarProgress(inventory).SeasonProgress.ActivatedChallenges, Name);
|
||||
}
|
||||
});
|
||||
|
||||
if ((Completed?.length ?? 0) > (dbChallenge.Completed?.length ?? 0)) {
|
||||
dbChallenge.Completed ??= [];
|
||||
for (const completion of Completed!) {
|
||||
if (dbChallenge.Completed.indexOf(completion) == -1) {
|
||||
if (completion == "challengeRewards") {
|
||||
if (Name in challengeRewardsInboxMessages) {
|
||||
await createMessage(account._id, [challengeRewardsInboxMessages[Name]]);
|
||||
dbChallenge.Completed.push(completion);
|
||||
// Would love to somehow let the client know about inbox or inventory changes, but there doesn't seem to anything for updateChallengeProgress.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
logger.warn(`ignoring unknown challenge completion`, { challenge: Name, completion });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const affiliationMods: IAffiliationMods[] = [];
|
||||
if (SeasonChallengeCompletions) {
|
||||
@ -2102,6 +2188,21 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void =>
|
||||
inventory.LotusCustomization.syancol = {};
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let numFixed = 0;
|
||||
for (const equipmentKey of equipmentKeys) {
|
||||
for (const item of inventory[equipmentKey]) {
|
||||
if (item.ModularParts?.length === 0) {
|
||||
item.ModularParts = undefined;
|
||||
++numFixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numFixed != 0) {
|
||||
logger.debug(`removed ModularParts from ${numFixed} non-modular items`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
|
||||
|
@ -32,7 +32,7 @@ export const getUsernameFromEmail = async (email: string): Promise<string> => {
|
||||
name = nameFromEmail + suffix;
|
||||
} while (await isNameTaken(name));
|
||||
}
|
||||
return nameFromEmail;
|
||||
return name;
|
||||
};
|
||||
|
||||
export const createAccount = async (accountData: IDatabaseAccountRequiredFields): Promise<IDatabaseAccountJson> => {
|
||||
|
@ -292,7 +292,7 @@ export const addMissionInventoryUpdates = async (
|
||||
addRecipes(inventory, value);
|
||||
break;
|
||||
case "ChallengeProgress":
|
||||
addChallenges(account, inventory, value, inventoryUpdates.SeasonChallengeCompletions);
|
||||
await addChallenges(account, inventory, value, inventoryUpdates.SeasonChallengeCompletions);
|
||||
break;
|
||||
case "FusionTreasures":
|
||||
addFusionTreasures(inventory, value);
|
||||
@ -972,7 +972,8 @@ export const addMissionRewards = async (
|
||||
Missions: missions,
|
||||
RegularCredits: creditDrops,
|
||||
VoidTearParticipantsCurrWave: voidTearWave,
|
||||
StrippedItems: strippedItems
|
||||
StrippedItems: strippedItems,
|
||||
AffiliationChanges: AffiliationMods
|
||||
}: IMissionInventoryUpdateRequest,
|
||||
firstCompletion: boolean
|
||||
): Promise<AddMissionRewardsReturnType> => {
|
||||
@ -992,7 +993,6 @@ export const addMissionRewards = async (
|
||||
);
|
||||
logger.debug("random mission drops:", MissionRewards);
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
const AffiliationMods: IAffiliationMods[] = [];
|
||||
let SyndicateXPItemReward;
|
||||
let ConquestCompletedMissionsCount;
|
||||
|
||||
@ -1283,9 +1283,7 @@ export const addMissionRewards = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
let medallionAmount = Math.floor(
|
||||
Math.min(rewardInfo.JobStage, currentJob.xpAmounts.length - 1) / (rewardInfo.Q ? 0.8 : 1)
|
||||
);
|
||||
let medallionAmount = Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1));
|
||||
if (
|
||||
["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some(
|
||||
ending => jobType.endsWith(ending)
|
||||
@ -1304,6 +1302,9 @@ export const addMissionRewards = async (
|
||||
ItemCount: medallionAmount
|
||||
});
|
||||
SyndicateXPItemReward = medallionAmount;
|
||||
logger.debug(
|
||||
`Giving ${medallionAmount} medallions for the ${rewardInfo.JobStage} stage of the ${rewardInfo.JobTier} tier bounty`
|
||||
);
|
||||
} else {
|
||||
if (rewardInfo.JobTier! >= 0) {
|
||||
addStanding(
|
||||
@ -1333,6 +1334,9 @@ export const addMissionRewards = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jobType == "/Lotus/Types/Gameplay/Eidolon/Jobs/NewbieJob") {
|
||||
addStanding(inventory, "CetusSyndicate", Math.floor(200 / (rewardInfo.Q ? 0.8 : 1)), AffiliationMods);
|
||||
}
|
||||
}
|
||||
|
||||
if (rewardInfo.challengeMissionId) {
|
||||
@ -1349,6 +1353,7 @@ export const addMissionRewards = async (
|
||||
ItemCount: medallionAmount
|
||||
});
|
||||
SyndicateXPItemReward = medallionAmount;
|
||||
logger.debug(`Giving ${medallionAmount} medallions for the ${tier} tier bounty`);
|
||||
} else {
|
||||
let standingAmount = (tier + 1) * 1000;
|
||||
if (tier > 5) standingAmount = 7500; // InfestedLichBounty
|
||||
@ -1637,7 +1642,10 @@ function getRandomMissionDrops(
|
||||
rewardManifests = [
|
||||
"/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriMurmurFinalSteelChestRewards"
|
||||
];
|
||||
} else if (RewardInfo.T == 70) {
|
||||
} else if (
|
||||
RewardInfo.T == 70 ||
|
||||
RewardInfo.T == 6 // https://onlyg.it/OpenWF/SpaceNinjaServer/issues/2526
|
||||
) {
|
||||
// Orowyrm chest, gives 10 Pathos Clamps, or 15 on Steel Path.
|
||||
drops.push({
|
||||
StoreItem: "/Lotus/StoreItems/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
|
||||
@ -1771,6 +1779,11 @@ function getRandomMissionDrops(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jobType == "/Lotus/Types/Gameplay/Eidolon/Jobs/NewbieJob") {
|
||||
rewardManifests = ["/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATableARewards"];
|
||||
rotations = [3];
|
||||
if (RewardInfo.Q) rotations.push(3);
|
||||
}
|
||||
}
|
||||
} else if (RewardInfo.challengeMissionId) {
|
||||
const rewardTables: Record<string, string[]> = {
|
||||
|
@ -581,7 +581,7 @@ const handleBoosterPackPurchase = async (
|
||||
purchaseResponse.InventoryChanges,
|
||||
await addItem(inventory, specialItemReward.Item)
|
||||
);
|
||||
// TOVERIFY: Is the SpecialItemRewardAttenuation entry removed now?
|
||||
atten.Atten = 0;
|
||||
} else {
|
||||
atten.Atten += specialItemReward.PityIncreaseRate!;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { getShip } from "@/src/services/shipService";
|
||||
import {
|
||||
IResetShipDecorationsRequest,
|
||||
IResetShipDecorationsResponse,
|
||||
ISetPlacedDecoInfoRequest,
|
||||
ISetShipCustomizationsRequest,
|
||||
IShipDecorationsRequest,
|
||||
@ -52,12 +54,7 @@ export const handleSetShipDecorations = async (
|
||||
): Promise<IShipDecorationsResponse> => {
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
|
||||
const rooms =
|
||||
placedDecoration.BootLocation == "SHOP"
|
||||
? personalRooms.TailorShop.Rooms
|
||||
: placedDecoration.IsApartment
|
||||
? personalRooms.Apartment.Rooms
|
||||
: personalRooms.Ship.Rooms;
|
||||
const rooms = getRoomsForBootLocation(personalRooms, placedDecoration);
|
||||
|
||||
const roomToPlaceIn = rooms.find(room => room.Name === placedDecoration.Room);
|
||||
|
||||
@ -159,7 +156,6 @@ export const handleSetShipDecorations = async (
|
||||
|
||||
if (!config.unlockAllShipDecorations) {
|
||||
const inventory = await getInventory(accountId);
|
||||
const itemType = Object.entries(ExportResources).find(arr => arr[1].deco == placedDecoration.Type)![0];
|
||||
if (placedDecoration.Sockets !== undefined) {
|
||||
addFusionTreasures(inventory, [{ ItemType: itemType, Sockets: placedDecoration.Sockets, ItemCount: -1 }]);
|
||||
} else {
|
||||
@ -192,17 +188,62 @@ export const handleSetShipDecorations = async (
|
||||
|
||||
const getRoomsForBootLocation = (
|
||||
personalRooms: TPersonalRoomsDatabaseDocument,
|
||||
bootLocation: TBootLocation | undefined
|
||||
request: { BootLocation?: TBootLocation; IsApartment?: boolean }
|
||||
): RoomsType[] => {
|
||||
if (bootLocation == "SHOP") {
|
||||
if (request.BootLocation == "SHOP") {
|
||||
return personalRooms.TailorShop.Rooms;
|
||||
}
|
||||
if (bootLocation == "APARTMENT") {
|
||||
if (request.BootLocation == "APARTMENT" || request.IsApartment) {
|
||||
return personalRooms.Apartment.Rooms;
|
||||
}
|
||||
return personalRooms.Ship.Rooms;
|
||||
};
|
||||
|
||||
export const handleResetShipDecorations = async (
|
||||
accountId: string,
|
||||
request: IResetShipDecorationsRequest
|
||||
): Promise<IResetShipDecorationsResponse> => {
|
||||
const [personalRooms, inventory] = await Promise.all([getPersonalRooms(accountId), getInventory(accountId)]);
|
||||
const room = getRoomsForBootLocation(personalRooms, request).find(room => room.Name === request.Room);
|
||||
if (!room) {
|
||||
throw new Error(`unknown room: ${request.Room}`);
|
||||
}
|
||||
|
||||
for (const deco of room.PlacedDecos) {
|
||||
const entry = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type);
|
||||
if (!entry) {
|
||||
throw new Error(`unknown deco type: ${deco.Type}`);
|
||||
}
|
||||
const [itemType, meta] = entry;
|
||||
if (meta.capacityCost === undefined) {
|
||||
throw new Error(`unknown deco type: ${deco.Type}`);
|
||||
}
|
||||
|
||||
// refund item
|
||||
if (!config.unlockAllShipDecorations) {
|
||||
if (deco.Sockets !== undefined) {
|
||||
addFusionTreasures(inventory, [{ ItemType: itemType, Sockets: deco.Sockets, ItemCount: 1 }]);
|
||||
} else {
|
||||
addShipDecorations(inventory, [{ ItemType: itemType, ItemCount: 1 }]);
|
||||
}
|
||||
}
|
||||
|
||||
// refund capacity
|
||||
room.MaxCapacity += meta.capacityCost;
|
||||
}
|
||||
|
||||
// empty room
|
||||
room.PlacedDecos.splice(0, room.PlacedDecos.length);
|
||||
|
||||
await Promise.all([personalRooms.save(), inventory.save()]);
|
||||
|
||||
return {
|
||||
ResetRoom: request.Room,
|
||||
ClaimedDecos: [], // Not sure what this is for; the client already implies that the decos were returned to inventory.
|
||||
NewCapacity: room.MaxCapacity
|
||||
};
|
||||
};
|
||||
|
||||
export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlacedDecoInfoRequest): Promise<void> => {
|
||||
if (req.GuildId && req.ComponentId) {
|
||||
const guild = (await Guild.findById(req.GuildId))!;
|
||||
@ -217,7 +258,7 @@ export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlaced
|
||||
|
||||
const personalRooms = await getPersonalRooms(accountId);
|
||||
|
||||
const room = getRoomsForBootLocation(personalRooms, req.BootLocation).find(room => room.Name === req.Room);
|
||||
const room = getRoomsForBootLocation(personalRooms, req).find(room => room.Name === req.Room);
|
||||
if (!room) {
|
||||
throw new Error(`unknown room: ${req.Room}`);
|
||||
}
|
||||
|
@ -261,7 +261,8 @@ export const getSortie = (day: number): ISortie => {
|
||||
if (
|
||||
sortieFactionToSystemIndexes[sortieBossToFaction[boss]].includes(value.systemIndex) &&
|
||||
sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) &&
|
||||
key in sortieTilesets
|
||||
key in sortieTilesets &&
|
||||
(key != "SolNode228" || sortieBossToFaction[boss] == "FC_GRINEER") // PoE does not work for non-infested enemies
|
||||
) {
|
||||
nodes.push(key);
|
||||
}
|
||||
@ -1037,13 +1038,13 @@ const pushVoidStorms = (arr: IVoidStorm[], hour: number): void => {
|
||||
};
|
||||
|
||||
interface ITimeConstraint {
|
||||
//name: string;
|
||||
name: string;
|
||||
isValidTime: (timeSecs: number) => boolean;
|
||||
getIdealTimeBefore: (timeSecs: number) => number;
|
||||
}
|
||||
|
||||
const eidolonDayConstraint: ITimeConstraint = {
|
||||
//name: "eidolon day",
|
||||
name: "eidolon day",
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const eidolonEpoch = 1391992660;
|
||||
const eidolonCycle = Math.trunc((timeSecs - eidolonEpoch) / 9000);
|
||||
@ -1061,7 +1062,7 @@ const eidolonDayConstraint: ITimeConstraint = {
|
||||
};
|
||||
|
||||
const eidolonNightConstraint: ITimeConstraint = {
|
||||
//name: "eidolon night",
|
||||
name: "eidolon night",
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const eidolonEpoch = 1391992660;
|
||||
const eidolonCycle = Math.trunc((timeSecs - eidolonEpoch) / 9000);
|
||||
@ -1088,7 +1089,7 @@ const eidolonNightConstraint: ITimeConstraint = {
|
||||
};
|
||||
|
||||
const venusColdConstraint: ITimeConstraint = {
|
||||
//name: "venus cold",
|
||||
name: "venus cold",
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const vallisEpoch = 1541837628;
|
||||
const vallisCycle = Math.trunc((timeSecs - vallisEpoch) / 1600);
|
||||
@ -1114,7 +1115,7 @@ const venusColdConstraint: ITimeConstraint = {
|
||||
};
|
||||
|
||||
const venusWarmConstraint: ITimeConstraint = {
|
||||
//name: "venus warm",
|
||||
name: "venus warm",
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const vallisEpoch = 1541837628;
|
||||
const vallisCycle = Math.trunc((timeSecs - vallisEpoch) / 1600);
|
||||
@ -1156,6 +1157,25 @@ const getIdealTimeSatsifyingConstraints = (constraints: ITimeConstraint[]): numb
|
||||
return timeSecs;
|
||||
};
|
||||
|
||||
const fullyStockBaro = (vt: IVoidTrader): void => {
|
||||
for (const armorSet of baro.armorSets) {
|
||||
if (Array.isArray(armorSet[0])) {
|
||||
for (const set of armorSet as IVoidTraderOffer[][]) {
|
||||
for (const item of set) {
|
||||
vt.Manifest.push(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const item of armorSet as IVoidTraderOffer[]) {
|
||||
vt.Manifest.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const item of baro.rest) {
|
||||
vt.Manifest.push(item);
|
||||
}
|
||||
};
|
||||
|
||||
const getVarziaRotation = (week: number): string => {
|
||||
const seed = new SRng(week).randomInt(0, 100_000);
|
||||
const rng = new SRng(seed);
|
||||
@ -1320,7 +1340,7 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
});
|
||||
} else {
|
||||
constraints.push({
|
||||
//name: `duviri ${config.worldState.duviriOverride}`,
|
||||
name: `duviri ${config.worldState.duviriOverride}`,
|
||||
isValidTime: (timeSecs: number): boolean => {
|
||||
const moodIndex = Math.trunc(timeSecs / 7200);
|
||||
return moodIndex % 5 == desiredMood;
|
||||
@ -1335,11 +1355,20 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
}
|
||||
}
|
||||
const timeSecs = getIdealTimeSatsifyingConstraints(constraints);
|
||||
if (constraints.length != 0) {
|
||||
const delta = Math.trunc(Date.now() / 1000) - timeSecs;
|
||||
if (delta > 1) {
|
||||
logger.debug(
|
||||
`reported time is ${delta} seconds behind real time to satisfy selected constraints (${constraints.map(x => x.name).join(", ")})`
|
||||
);
|
||||
}
|
||||
}
|
||||
const timeMs = timeSecs * 1000;
|
||||
const day = Math.trunc((timeMs - EPOCH) / 86400000);
|
||||
const week = Math.trunc(day / 7);
|
||||
const weekStart = EPOCH + week * 604800000;
|
||||
const weekEnd = weekStart + 604800000;
|
||||
const date = new Date(timeMs);
|
||||
|
||||
const worldState: IWorldState = {
|
||||
BuildLabel: typeof buildLabel == "string" ? buildLabel.split(" ").join("+") : buildConfig.buildLabel,
|
||||
@ -1366,11 +1395,77 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
worldState.PVPChallengeInstances = [];
|
||||
}
|
||||
|
||||
if (config.worldState?.starDays) {
|
||||
if (config.worldState?.tennoLiveRelay) {
|
||||
worldState.Goals.push({
|
||||
_id: {
|
||||
$oid: "687bf9400000000000000000"
|
||||
},
|
||||
Activation: {
|
||||
$date: {
|
||||
$numberLong: "1752955200000"
|
||||
}
|
||||
},
|
||||
Expiry: {
|
||||
$date: {
|
||||
$numberLong: "2000000000000"
|
||||
}
|
||||
},
|
||||
Count: 0,
|
||||
Goal: 0,
|
||||
Success: 0,
|
||||
Personal: true,
|
||||
Desc: "/Lotus/Language/Locations/RelayStationTennoConB",
|
||||
ToolTip: "/Lotus/Language/Locations/RelayStationTennoConDescB",
|
||||
Icon: "/Lotus/Interface/Icons/Categories/IconTennoLive.png",
|
||||
Tag: "TennoConRelayB",
|
||||
Node: "TennoConBHUB6"
|
||||
});
|
||||
}
|
||||
if (config.worldState?.baroTennoConRelay) {
|
||||
worldState.Goals.push({
|
||||
_id: { $oid: "687bb2f00000000000000000" },
|
||||
Activation: { $date: { $numberLong: "1752937200000" } },
|
||||
Expiry: { $date: { $numberLong: "2000000000000" } },
|
||||
Count: 0,
|
||||
Goal: 0,
|
||||
Success: 0,
|
||||
Personal: true,
|
||||
//"Faction": "FC_GRINEER",
|
||||
Desc: "/Lotus/Language/Locations/RelayStationTennoCon",
|
||||
ToolTip: "/Lotus/Language/Locations/RelayStationTennoConDesc",
|
||||
Icon: "/Lotus/Interface/Icons/Categories/IconTennoConSigil.png",
|
||||
Tag: "TennoConRelay",
|
||||
Node: "TennoConHUB2"
|
||||
});
|
||||
const vt: IVoidTrader = {
|
||||
_id: { $oid: "687809030379266d790495c6" },
|
||||
Activation: { $date: { $numberLong: "1752937200000" } },
|
||||
Expiry: { $date: { $numberLong: "2000000000000" } },
|
||||
Character: "Baro'Ki Teel",
|
||||
Node: "TennoConHUB2",
|
||||
Manifest: []
|
||||
};
|
||||
worldState.VoidTraders.push(vt);
|
||||
fullyStockBaro(vt);
|
||||
}
|
||||
const isFebruary = date.getUTCMonth() == 1;
|
||||
if (config.worldState?.starDaysOverride ?? isFebruary) {
|
||||
worldState.Goals.push({
|
||||
_id: { $oid: "67a4dcce2a198564d62e1647" },
|
||||
Activation: { $date: { $numberLong: "1738868400000" } },
|
||||
Expiry: { $date: { $numberLong: "2000000000000" } },
|
||||
Activation: {
|
||||
$date: {
|
||||
$numberLong: config.worldState?.starDaysOverride
|
||||
? "1738868400000"
|
||||
: Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1).toString()
|
||||
}
|
||||
},
|
||||
Expiry: {
|
||||
$date: {
|
||||
$numberLong: config.worldState?.starDaysOverride
|
||||
? "2000000000000"
|
||||
: Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1).toString()
|
||||
}
|
||||
},
|
||||
Count: 0,
|
||||
Goal: 0,
|
||||
Success: 0,
|
||||
@ -1584,24 +1679,8 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
|
||||
};
|
||||
worldState.VoidTraders.push(vt);
|
||||
if (isBeforeNextExpectedWorldStateRefresh(timeMs, baroActualStart)) {
|
||||
vt.Manifest = [];
|
||||
if (config.baroFullyStocked) {
|
||||
for (const armorSet of baro.armorSets) {
|
||||
if (Array.isArray(armorSet[0])) {
|
||||
for (const set of armorSet as IVoidTraderOffer[][]) {
|
||||
for (const item of set) {
|
||||
vt.Manifest.push(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const item of armorSet as IVoidTraderOffer[]) {
|
||||
vt.Manifest.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const item of baro.rest) {
|
||||
vt.Manifest.push(item);
|
||||
}
|
||||
fullyStockBaro(vt);
|
||||
} else {
|
||||
const rng = new SRng(new SRng(baroIndex).randomInt(0, 100_000));
|
||||
// TOVERIFY: Constraint for upgrades amount?
|
||||
@ -1822,7 +1901,10 @@ export const populateFissures = async (worldState: IWorldState): Promise<void> =
|
||||
_id: toOid(fissure._id),
|
||||
Region: meta.systemIndex + 1,
|
||||
Seed: 1337,
|
||||
Activation: toMongoDate(fissure.Activation),
|
||||
Activation:
|
||||
fissure.Activation.getTime() < Date.now() // Activation is in the past?
|
||||
? { $date: { $numberLong: "1000000000000" } } // Let the client know 'explicitly' to avoid interference from time constraints.
|
||||
: toMongoDate(fissure.Activation),
|
||||
Expiry: toMongoDate(fissure.Expiry),
|
||||
Node: fissure.Node,
|
||||
MissionType: eMissionType[meta.missionIndex].tag,
|
||||
|
@ -5,6 +5,7 @@ import { Account } from "@/src/models/loginModel";
|
||||
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "@/src/services/loginService";
|
||||
import { IDatabaseAccountJson } from "@/src/types/loginTypes";
|
||||
import { HydratedDocument } from "mongoose";
|
||||
import { logError } from "@/src/utils/logger";
|
||||
|
||||
let wsServer: ws.Server | undefined;
|
||||
let wssServer: ws.Server | undefined;
|
||||
@ -43,7 +44,7 @@ export const stopWsServers = (promises: Promise<void>[]): void => {
|
||||
let lastWsid: number = 0;
|
||||
|
||||
interface IWsCustomData extends ws {
|
||||
id?: number;
|
||||
id: number;
|
||||
accountId?: string;
|
||||
}
|
||||
|
||||
@ -88,63 +89,67 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
ws.on("message", async msg => {
|
||||
const data = JSON.parse(String(msg)) as IWsMsgFromClient;
|
||||
if (data.auth) {
|
||||
let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email });
|
||||
if (account) {
|
||||
if (isCorrectPassword(data.auth.password, account.password)) {
|
||||
if (!account.Nonce) {
|
||||
account.ClientType = "webui";
|
||||
account.Nonce = createNonce();
|
||||
await (account as HydratedDocument<IDatabaseAccountJson>).save();
|
||||
try {
|
||||
const data = JSON.parse(String(msg)) as IWsMsgFromClient;
|
||||
if (data.auth) {
|
||||
let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email });
|
||||
if (account) {
|
||||
if (isCorrectPassword(data.auth.password, account.password)) {
|
||||
if (!account.Nonce) {
|
||||
account.ClientType = "webui";
|
||||
account.Nonce = createNonce();
|
||||
await (account as HydratedDocument<IDatabaseAccountJson>).save();
|
||||
}
|
||||
} else {
|
||||
account = null;
|
||||
}
|
||||
} else if (data.auth.isRegister) {
|
||||
const name = await getUsernameFromEmail(data.auth.email);
|
||||
account = await createAccount({
|
||||
email: data.auth.email,
|
||||
password: data.auth.password,
|
||||
ClientType: "webui",
|
||||
LastLogin: new Date(),
|
||||
DisplayName: name,
|
||||
Nonce: createNonce()
|
||||
});
|
||||
}
|
||||
if (account) {
|
||||
(ws as IWsCustomData).accountId = account.id;
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
auth_succ: {
|
||||
id: account.id,
|
||||
DisplayName: account.DisplayName,
|
||||
Nonce: account.Nonce
|
||||
}
|
||||
} satisfies IWsMsgToClient)
|
||||
);
|
||||
} else {
|
||||
account = null;
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
auth_fail: {
|
||||
isRegister: data.auth.isRegister
|
||||
}
|
||||
} satisfies IWsMsgToClient)
|
||||
);
|
||||
}
|
||||
} else if (data.auth.isRegister) {
|
||||
const name = await getUsernameFromEmail(data.auth.email);
|
||||
account = await createAccount({
|
||||
email: data.auth.email,
|
||||
password: data.auth.password,
|
||||
ClientType: "webui",
|
||||
LastLogin: new Date(),
|
||||
DisplayName: name,
|
||||
Nonce: createNonce()
|
||||
});
|
||||
}
|
||||
if (account) {
|
||||
(ws as IWsCustomData).accountId = account.id;
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
auth_succ: {
|
||||
id: account.id,
|
||||
DisplayName: account.DisplayName,
|
||||
Nonce: account.Nonce
|
||||
}
|
||||
} satisfies IWsMsgToClient)
|
||||
);
|
||||
} else {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
auth_fail: {
|
||||
isRegister: data.auth.isRegister
|
||||
}
|
||||
} satisfies IWsMsgToClient)
|
||||
if (data.logout) {
|
||||
const accountId = (ws as IWsCustomData).accountId;
|
||||
(ws as IWsCustomData).accountId = undefined;
|
||||
await Account.updateOne(
|
||||
{
|
||||
_id: accountId,
|
||||
ClientType: "webui"
|
||||
},
|
||||
{
|
||||
Nonce: 0
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if (data.logout) {
|
||||
const accountId = (ws as IWsCustomData).accountId;
|
||||
(ws as IWsCustomData).accountId = undefined;
|
||||
await Account.updateOne(
|
||||
{
|
||||
_id: accountId,
|
||||
ClientType: "webui"
|
||||
},
|
||||
{
|
||||
Nonce: 0
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e as Error, `processing websocket message`);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -181,18 +186,24 @@ export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void
|
||||
}
|
||||
};
|
||||
|
||||
export const sendWsBroadcastExcept = (wsid: number | undefined, data: IWsMsgToClient): void => {
|
||||
export const sendWsBroadcastEx = (data: IWsMsgToClient, accountId?: string, excludeWsid?: number): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
if (wsServer) {
|
||||
for (const client of wsServer.clients) {
|
||||
if ((client as IWsCustomData).id != wsid) {
|
||||
if (
|
||||
(!accountId || (client as IWsCustomData).accountId == accountId) &&
|
||||
(client as IWsCustomData).id != excludeWsid
|
||||
) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wssServer) {
|
||||
for (const client of wssServer.clients) {
|
||||
if ((client as IWsCustomData).id != wsid) {
|
||||
if (
|
||||
(!accountId || (client as IWsCustomData).accountId == accountId) &&
|
||||
(client as IWsCustomData).id != excludeWsid
|
||||
) {
|
||||
client.send(msg);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Types } from "mongoose";
|
||||
import { IOid, IMongoDate, IOidWithLegacySupport, ITypeCount } from "@/src/types/commonTypes";
|
||||
import {
|
||||
@ -216,6 +215,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
PremiumCredits: number;
|
||||
PremiumCreditsFree: number;
|
||||
FusionPoints: number;
|
||||
CrewShipFusionPoints: number; //Dirac (pre-rework Railjack)
|
||||
PrimeTokens: number;
|
||||
SuitBin: ISlots;
|
||||
WeaponBin: ISlots;
|
||||
|
@ -91,12 +91,16 @@ export interface IApartmentClient {
|
||||
Gardening: IGardeningClient;
|
||||
Rooms: IRoom[];
|
||||
FavouriteLoadouts: IFavouriteLoadout[];
|
||||
VideoWallBackdrop?: string;
|
||||
Soundscape?: string;
|
||||
}
|
||||
|
||||
export interface IApartmentDatabase {
|
||||
Gardening: IGardeningDatabase;
|
||||
Rooms: IRoom[];
|
||||
FavouriteLoadouts: IFavouriteLoadoutDatabase[];
|
||||
VideoWallBackdrop?: string;
|
||||
Soundscape?: string;
|
||||
}
|
||||
|
||||
export interface IPlacedDecosDatabase {
|
||||
@ -150,6 +154,17 @@ export interface IShipDecorationsResponse {
|
||||
NewRoom?: string;
|
||||
}
|
||||
|
||||
export interface IResetShipDecorationsRequest {
|
||||
Room: string;
|
||||
BootLocation?: TBootLocation;
|
||||
}
|
||||
|
||||
export interface IResetShipDecorationsResponse {
|
||||
ResetRoom: string;
|
||||
ClaimedDecos: [];
|
||||
NewCapacity: number;
|
||||
}
|
||||
|
||||
export interface ISetPlacedDecoInfoRequest {
|
||||
DecoType: string;
|
||||
DecoId: string;
|
||||
|
@ -74,6 +74,7 @@ export type IInventoryChanges = {
|
||||
InfestedFoundry?: IInfestedFoundryClient;
|
||||
Drones?: IDroneClient[];
|
||||
MiscItems?: IMiscItem[];
|
||||
ShipDecorations?: ITypeCount[];
|
||||
EmailItems?: ITypeCount[];
|
||||
CrewShipRawSalvage?: ITypeCount[];
|
||||
Nemesis?: Partial<INemesisClient>;
|
||||
|
@ -79,6 +79,7 @@ export interface ILoadoutDatabase {
|
||||
NORMAL_PVP: ILoadoutConfigDatabase[];
|
||||
LUNARO: ILoadoutConfigDatabase[];
|
||||
OPERATOR: ILoadoutConfigDatabase[];
|
||||
GEAR: ILoadoutConfigDatabase[];
|
||||
KDRIVE: ILoadoutConfigDatabase[];
|
||||
DATAKNIFE: ILoadoutConfigDatabase[];
|
||||
MECH: ILoadoutConfigDatabase[];
|
||||
|
@ -108,3 +108,13 @@ errorLog.on("new", filename => logger.info(`Using error log file: ${filename}`))
|
||||
combinedLog.on("new", filename => logger.info(`Using combined log file: ${filename}`));
|
||||
errorLog.on("rotate", filename => logger.info(`Rotated error log file: ${filename}`));
|
||||
combinedLog.on("rotate", filename => logger.info(`Rotated combined log file: ${filename}`));
|
||||
|
||||
export const logError = (err: Error, context: string): void => {
|
||||
if (err.stack) {
|
||||
const stackArr = err.stack.split("\n");
|
||||
stackArr[0] += ` while ${context}`;
|
||||
logger.error(stackArr.join("\n"));
|
||||
} else {
|
||||
logger.error(`uncaught error while ${context}: ${err.message}`);
|
||||
}
|
||||
};
|
||||
|
@ -304,7 +304,6 @@
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Seasonal/AvatarImageGlyphCookieKubrow", "PrimePrice": 80, "RegularPrice": 50000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/LisetScarf", "PrimePrice": 600, "RegularPrice": 400000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/SuitCustomizations/ColourPickerTwitchBItemA", "PrimePrice": 220, "RegularPrice": 220000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/FootstepsMaple", "PrimePrice": 15, "RegularPrice": 1000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/BaroKavatBadgeItem", "PrimePrice": 50, "RegularPrice": 50000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/BaroKavatSigil", "PrimePrice": 55, "RegularPrice": 45000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/WraithTurbinesScarf", "PrimePrice": 400, "RegularPrice": 500000 },
|
||||
@ -363,7 +362,6 @@
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileInarosTomb", "PrimePrice": 325, "RegularPrice": 175000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/BaroFireWorksCrate", "PrimePrice": 50, "RegularPrice": 100000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileOrokinExtraction", "PrimePrice": 325, "RegularPrice": 175000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Keys/MummyQuestKeyBlueprint", "PrimePrice": 100, "RegularPrice": 25000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/AssassinBait", "PrimePrice": 200, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/AssassinBaitB", "PrimePrice": 200, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationB", "PrimePrice": 100, "RegularPrice": 100000 },
|
||||
@ -401,7 +399,39 @@
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationC", "PrimePrice": 100, "RegularPrice": 100000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/PedistalPrime", "PrimePrice": 0, "RegularPrice": 1000000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/Emotes/BaroEmote", "PrimePrice": 0, "RegularPrice": 1000000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/EventSniperReloadDamageMod", "PrimePrice": 2995, "RegularPrice": 1000000 }
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/EventSniperReloadDamageMod", "PrimePrice": 2995, "RegularPrice": 1000000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageAvaClemCommunityGlyph", "PrimePrice": 20, "RegularPrice": 33333 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TennoconConcert2025Display", "PrimePrice": 90, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/SummerGameFestPoster", "PrimePrice": 90, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/RathuumEventPoster", "PrimePrice": 90, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionCorpus", "PrimePrice": 70, "RegularPrice": 55000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionEntrati", "PrimePrice": 99, "RegularPrice": 1900 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionScaldra", "PrimePrice": 93, "RegularPrice": 1906 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Factions/GlyphFactionTechrot", "PrimePrice": 98, "RegularPrice": 1901 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Warframes/VorunaActionGlyph", "PrimePrice": 75, "RegularPrice": 60000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageVoidAngelBaro", "PrimePrice": 80, "RegularPrice": 50000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/1999DrippySigil", "PrimePrice": 50, "RegularPrice": 45000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Weapons/Rapier/CrpRapierSkin", "PrimePrice": 375, "RegularPrice": 400000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Events/OgrisOldSchool", "PrimePrice": 350, "RegularPrice": 325000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/PrismaLotusFlamesSigil", "PrimePrice": 55, "RegularPrice": 60000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeFactionDamageMurmursExpert", "PrimePrice": 375, "RegularPrice": 130000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponRecoilReductionModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/TenthAnniversaryLoginSongItem", "PrimePrice": 145, "RegularPrice": 165000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/AbyssofDagathSongItem", "PrimePrice": 150, "RegularPrice": 155000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/ZarimanLoginSongItem", "PrimePrice": 160, "RegularPrice": 180000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/DanteUnboundLoginSongItem", "PrimePrice": 150, "RegularPrice": 150000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/EmpyreanSongItem", "PrimePrice": 160, "RegularPrice": 155000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/DeimosLoginSongItem", "PrimePrice": 155, "RegularPrice": 160000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/JadeShadowsLoginSongItem", "PrimePrice": 150, "RegularPrice": 170000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/WhispersInTheWallLoginSongItem", "PrimePrice": 165, "RegularPrice": 170000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/CorpusRailjackLoginSongItem", "PrimePrice": 150, "RegularPrice": 165000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/LotusEatersSongItem", "PrimePrice": 165, "RegularPrice": 150000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/SongItems/KuvaLichLoginSongItem", "PrimePrice": 140, "RegularPrice": 170000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyBaro", "PrimePrice": 100, "RegularPrice": 125000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyInaros", "PrimePrice": 120, "RegularPrice": 90000 },
|
||||
{ "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponPistolFactionDamageMurmursExpert", "PrimePrice": 375, "RegularPrice": 130000 }
|
||||
],
|
||||
"allIfAny": [
|
||||
[
|
||||
|
@ -388,11 +388,11 @@
|
||||
<div class="card" style="height: 400px;">
|
||||
<h5 class="card-header" data-loc="inventory_hoverboards"></h5>
|
||||
<div class="card-body overflow-auto">
|
||||
<form class="input-group mb-3" onsubmit="doAcquireModularEquipment('HoverBoards');return false;">
|
||||
<input class="form-control" id="acquire-type-HoverBoards-HB_DECK" list="datalist-ModularParts-HB_DECK" />
|
||||
<input class="form-control" id="acquire-type-HoverBoards-HB_ENGINE" list="datalist-ModularParts-HB_ENGINE" />
|
||||
<input class="form-control" id="acquire-type-HoverBoards-HB_FRONT" list="datalist-ModularParts-HB_FRONT" />
|
||||
<input class="form-control" id="acquire-type-HoverBoards-HB_JET" list="datalist-ModularParts-HB_JET" />
|
||||
<form class="input-group mb-3" onsubmit="doAcquireModularEquipment('Hoverboards');return false;">
|
||||
<input class="form-control" id="acquire-type-Hoverboards-HB_DECK" list="datalist-ModularParts-HB_DECK" />
|
||||
<input class="form-control" id="acquire-type-Hoverboards-HB_ENGINE" list="datalist-ModularParts-HB_ENGINE" />
|
||||
<input class="form-control" id="acquire-type-Hoverboards-HB_FRONT" list="datalist-ModularParts-HB_FRONT" />
|
||||
<input class="form-control" id="acquire-type-Hoverboards-HB_JET" list="datalist-ModularParts-HB_JET" />
|
||||
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
|
||||
</form>
|
||||
<table class="table table-hover w-100">
|
||||
@ -436,22 +436,22 @@
|
||||
<h5 class="card-header" data-loc="general_bulkActions"></h5>
|
||||
<div class="card-body">
|
||||
<div class="mb-2 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['Suits']);" data-loc="inventory_bulkAddSuits"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['Melee', 'LongGuns', 'Pistols']);" data-loc="inventory_bulkAddWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['SpaceSuits']);" data-loc="inventory_bulkAddSpaceSuits"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkAddSpaceWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['Sentinels']);" data-loc="inventory_bulkAddSentinels"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEquipment(['SentinelWeapons']);" data-loc="inventory_bulkAddSentinelWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="addMissingEvolutionProgress();" data-loc="inventory_bulkAddEvolutionProgress"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['Suits']);" data-loc="inventory_bulkAddSuits"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['Melee', 'LongGuns', 'Pistols']);" data-loc="inventory_bulkAddWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['SpaceSuits']);" data-loc="inventory_bulkAddSpaceSuits"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkAddSpaceWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['Sentinels']);" data-loc="inventory_bulkAddSentinels"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEquipment, ['SentinelWeapons']);" data-loc="inventory_bulkAddSentinelWeapons"></button>
|
||||
<button class="btn btn-primary" onclick="debounce(addMissingEvolutionProgress);" data-loc="inventory_bulkAddEvolutionProgress"></button>
|
||||
</div>
|
||||
<div class="mb-2 d-flex flex-wrap gap-2">
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['Suits']);" data-loc="inventory_bulkRankUpSuits"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['Melee', 'LongGuns', 'Pistols']);" data-loc="inventory_bulkRankUpWeapons"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceSuits']);" data-loc="inventory_bulkRankUpSpaceSuits"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkRankUpSpaceWeapons"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['Sentinels']);" data-loc="inventory_bulkRankUpSentinels"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEquipment(['SentinelWeapons']);" data-loc="inventory_bulkRankUpSentinelWeapons"></button>
|
||||
<button class="btn btn-success" onclick="maxRankAllEvolutions();" data-loc="inventory_bulkRankUpEvolutionProgress"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['Suits']);" data-loc="inventory_bulkRankUpSuits"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['Melee', 'LongGuns', 'Pistols']);" data-loc="inventory_bulkRankUpWeapons"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['SpaceSuits']);" data-loc="inventory_bulkRankUpSpaceSuits"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['SpaceGuns', 'SpaceMelee']);" data-loc="inventory_bulkRankUpSpaceWeapons"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['Sentinels']);" data-loc="inventory_bulkRankUpSentinels"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEquipment, ['SentinelWeapons']);" data-loc="inventory_bulkRankUpSentinelWeapons"></button>
|
||||
<button class="btn btn-success" onclick="debounce(maxRankAllEvolutions);" data-loc="inventory_bulkRankUpEvolutionProgress"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -460,6 +460,13 @@
|
||||
<h3 id="detailedView-loading" class="mb-0" data-loc="general_loading"></h3>
|
||||
<h3 id="detailedView-title" class="mb-0"></h3>
|
||||
<p class="text-body-secondary"></p>
|
||||
<div id="loadout-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_loadoutLabel"></h5>
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs" id="loadoutTabs"></ul>
|
||||
<div class="tab-content mt-3" id="loadoutTabsContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="archonShards-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_archonShardsLabel"></h5>
|
||||
<div class="card-body">
|
||||
@ -478,6 +485,62 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="edit-suit-invigorations-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_suitInvigorationLabel"></h5>
|
||||
<div class="card-body">
|
||||
<form onsubmit="submitSuitInvigorationUpgrade(event)">
|
||||
<div class="mb-3">
|
||||
<label for="invigoration-offensive" class="form-label" data-loc="invigorations_offensiveLabel"></label>
|
||||
<select class="form-select" id="dv-invigoration-offensive">
|
||||
<option value="" data-loc="general_none"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerStrength" data-loc="invigorations_offensive_AbilityStrength"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerRange" data-loc="invigorations_offensive_AbilityRange"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPowerDuration" data-loc="invigorations_offensive_AbilityDuration"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationMeleeDamage" data-loc="invigorations_offensive_MeleeDamage"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPrimaryDamage" data-loc="invigorations_offensive_PrimaryDamage"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationSecondaryDamage" data-loc="invigorations_offensive_SecondaryDamage"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationPrimaryCritChance" data-loc="invigorations_offensive_PrimaryCritChance"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationSecondaryCritChance" data-loc="invigorations_offensive_SecondaryCritChance"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Offensive/OffensiveInvigorationMeleeCritChance" data-loc="invigorations_offensive_MeleeCritChance"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="invigoration-defensive" class="form-label" data-loc="invigorations_defensiveLabel"></label>
|
||||
<select class="form-select" id="dv-invigoration-defensive">
|
||||
<option value="" data-loc="general_none"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationPowerEfficiency" data-loc="invigorations_utility_AbilityEfficiency"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationMovementSpeed" data-loc="invigorations_utility_SprintSpeed"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationParkourSpeed" data-loc="invigorations_utility_ParkourVelocity"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationHealth" data-loc="invigorations_utility_HealthMax"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationEnergy" data-loc="invigorations_utility_EnergyMax"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationStatusResistance" data-loc="invigorations_utility_StatusImmune"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationReloadSpeed" data-loc="invigorations_utility_ReloadSpeed"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationHealthRegen" data-loc="invigorations_utility_HealthRegen"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationArmor" data-loc="invigorations_utility_ArmorMax"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationJumps" data-loc="invigorations_utility_Jumps"></option>
|
||||
<option value="/Lotus/Upgrades/Invigorations/Utility/UtilityInvigorationEnergyRegen" data-loc="invigorations_utility_EnergyRegen"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="invigoration-expiry" class="form-label" data-loc="invigorations_expiryLabel"></label>
|
||||
<input type="datetime-local" class="form-control" id="dv-invigoration-expiry" />
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary" data-loc="general_setButton"></button>
|
||||
<button type="button" class="btn btn-danger" onclick="clearSuitInvigorationUpgrades()" data-loc="code_remove"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modularParts-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_modularPartsLabel"></h5>
|
||||
<div class="card-body">
|
||||
<form id="modularParts-form" class="input-group mb-3" onsubmit="handleModularPartsChange(event)"></form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="valenceBonus-card" class="card mb-3 d-none">
|
||||
<h5 class="card-header" data-loc="detailedView_valenceBonusLabel"></h5>
|
||||
<div class="card-body">
|
||||
@ -863,8 +926,12 @@
|
||||
<label class="form-check-label" for="worldState.resourceBoost" data-loc="worldState_resourceBoost"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.starDays" />
|
||||
<label class="form-check-label" for="worldState.starDays" data-loc="worldState_starDays"></label>
|
||||
<input class="form-check-input" type="checkbox" id="worldState.tennoLiveRelay" />
|
||||
<label class="form-check-label" for="worldState.tennoLiveRelay" data-loc="worldState_tennoLiveRelay"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.baroTennoConRelay" />
|
||||
<label class="form-check-label" for="worldState.baroTennoConRelay" data-loc="worldState_baroTennoConRelay"></label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="worldState.varziaFullyStocked" />
|
||||
@ -879,6 +946,14 @@
|
||||
<option value="3" data-loc="worldState_we3"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label class="form-label" for="worldState.starDaysOverride" data-loc="worldState_starDays"></label>
|
||||
<select class="form-control" id="worldState.starDaysOverride" data-default="null">
|
||||
<option value="null" data-loc="normal"></option>
|
||||
<option value="true" data-loc="enabled"></option>
|
||||
<option value="false" data-loc="disabled"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<label class="form-label" for="worldState.eidolonOverride" data-loc="worldState_eidolonOverride"></label>
|
||||
<select class="form-control" id="worldState.eidolonOverride" data-default="">
|
||||
@ -1017,6 +1092,7 @@
|
||||
<datalist id="datalist-ModularParts-KUBROW_ANTIGEN"></datalist>
|
||||
<datalist id="datalist-ModularParts-KUBROW_MUTAGEN"></datalist>
|
||||
<datalist id="datalist-Boosters"></datalist>
|
||||
<datalist id="datalist-Abilities"></datalist>
|
||||
<datalist id="datalist-circuitGameModes">
|
||||
<option>Survival</option>
|
||||
<option>VoidFlood</option>
|
||||
|
@ -28,7 +28,8 @@ const sendAuth = isRegister => {
|
||||
};
|
||||
|
||||
function openWebSocket() {
|
||||
window.ws = new WebSocket("/custom/ws");
|
||||
const wsProto = location.protocol === "https:" ? "wss://" : "ws://";
|
||||
window.ws = new WebSocket(wsProto + location.host + "/custom/ws");
|
||||
window.ws.onopen = () => {
|
||||
ws_is_open = true;
|
||||
sendAuth(false);
|
||||
@ -118,9 +119,16 @@ function doLogin() {
|
||||
window.registerSubmit = false;
|
||||
}
|
||||
|
||||
async function revalidateAuthz() {
|
||||
await getWebSocket();
|
||||
// We have a websocket connection, so authz should be good.
|
||||
function revalidateAuthz() {
|
||||
return new Promise(resolve => {
|
||||
let interval;
|
||||
interval = setInterval(() => {
|
||||
if (ws_is_open && !auth_pending) {
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
}
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
|
||||
function logout() {
|
||||
@ -282,7 +290,8 @@ function fetchItemList() {
|
||||
});
|
||||
|
||||
const syndicateNone = document.createElement("option");
|
||||
syndicateNone.textContent = loc("cheats_none");
|
||||
syndicateNone.value = "";
|
||||
syndicateNone.textContent = loc("general_none");
|
||||
document.getElementById("changeSyndicate").innerHTML = "";
|
||||
document.getElementById("changeSyndicate").appendChild(syndicateNone);
|
||||
|
||||
@ -303,8 +312,8 @@ function fetchItemList() {
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityDurationMythic": loc("upgrade_WarframeAbilityDuration").split("|VAL|").join("15"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityStrength": loc("upgrade_WarframeAbilityStrength").split("|VAL|").join("10"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityStrengthMythic": loc("upgrade_WarframeAbilityStrength").split("|VAL|").join("15"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMax": loc("upgrade_WarframeArmourMax").split("|VAL|").join("150"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMaxMythic": loc("upgrade_WarframeArmourMax").split("|VAL|").join("225"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMax": loc("upgrade_WarframeArmorMax").split("|VAL|").join("150"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMaxMythic": loc("upgrade_WarframeArmorMax").split("|VAL|").join("225"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeBlastProc": loc("upgrade_WarframeBlastProc").split("|VAL|").join("5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeBlastProcMythic": loc("upgrade_WarframeBlastProc").split("|VAL|").join("7.5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCastingSpeed": loc("upgrade_WarframeCastingSpeed").split("|VAL|").join("25"),
|
||||
@ -333,8 +342,8 @@ function fetchItemList() {
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeParkourVelocityMythic": loc("upgrade_WarframeParkourVelocity").split("|VAL|").join("22.5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRadiationDamageBoost": loc("upgrade_WarframeRadiationDamageBoost").split("|VAL|").join("10"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRadiationDamageBoostMythic": loc("upgrade_WarframeRadiationDamageBoost").split("|VAL|").join("15"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegen": loc("upgrade_WarframeRegen").split("|VAL|").join("5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegenMythic": loc("upgrade_WarframeRegen").split("|VAL|").join("7.5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegen": loc("upgrade_WarframeHealthRegen").split("|VAL|").join("5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegenMythic": loc("upgrade_WarframeHealthRegen").split("|VAL|").join("7.5"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeShieldMax": loc("upgrade_WarframeShieldMax").split("|VAL|").join("150"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeShieldMaxMythic": loc("upgrade_WarframeShieldMax").split("|VAL|").join("225"),
|
||||
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeStartingEnergy": loc("upgrade_WarframeStartingEnergy").split("|VAL|").join("30"),
|
||||
@ -730,7 +739,10 @@ function updateInventory() {
|
||||
td.appendChild(a);
|
||||
}
|
||||
|
||||
if (["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category)) {
|
||||
if (
|
||||
["Suits", "LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category) ||
|
||||
modularWeapons.includes(item.ItemType)
|
||||
) {
|
||||
const a = document.createElement("a");
|
||||
a.href = "/webui/detailedView?productCategory=" + category + "&itemId=" + item.ItemId.$oid;
|
||||
a.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>`;
|
||||
@ -861,15 +873,11 @@ function updateInventory() {
|
||||
|
||||
const datalistEvolutionProgress = document.querySelectorAll("#datalist-EvolutionProgress option");
|
||||
const formEvolutionProgress = document.querySelector('form[onsubmit*="doAcquireEvolution()"]');
|
||||
const giveAllQEvolutionProgress = document.querySelector(
|
||||
'button[onclick*="addMissingEvolutionProgress()"]'
|
||||
);
|
||||
|
||||
if (datalistEvolutionProgress.length === 0) {
|
||||
formEvolutionProgress.classList.add("disabled");
|
||||
formEvolutionProgress.querySelector("input").disabled = true;
|
||||
formEvolutionProgress.querySelector("button").disabled = true;
|
||||
giveAllQEvolutionProgress.disabled = true;
|
||||
}
|
||||
|
||||
if (data.CrewShipHarnesses?.length) {
|
||||
@ -1017,13 +1025,19 @@ function updateInventory() {
|
||||
if (item.ItemType.substr(0, 32) == "/Lotus/Upgrades/Mods/Randomized/") {
|
||||
const rivenType = item.ItemType.substr(32);
|
||||
const fingerprint = JSON.parse(item.UpgradeFingerprint);
|
||||
if (fingerprint.buffs) {
|
||||
if ("buffs" in fingerprint) {
|
||||
// Riven has been revealed?
|
||||
const tr = document.createElement("tr");
|
||||
{
|
||||
const td = document.createElement("td");
|
||||
td.textContent = itemMap[fingerprint.compat]?.name ?? fingerprint.compat;
|
||||
td.textContent += " " + RivenParser.parseRiven(rivenType, fingerprint, 1).name;
|
||||
td.textContent += " ";
|
||||
try {
|
||||
td.textContent += RivenParser.parseRiven(rivenType, fingerprint, 1).name;
|
||||
} catch (e) {
|
||||
console.warn("malformed riven", { rivenType, fingerprint });
|
||||
td.textContent += " [Malformed Riven]";
|
||||
}
|
||||
td.innerHTML +=
|
||||
" <span title='" +
|
||||
loc("code_buffsNumber") +
|
||||
@ -1221,6 +1235,124 @@ function updateInventory() {
|
||||
}
|
||||
document.getElementById("crystals-list").appendChild(tr);
|
||||
});
|
||||
|
||||
document.getElementById("edit-suit-invigorations-card").classList.remove("d-none");
|
||||
const { OffensiveUpgrade, DefensiveUpgrade, UpgradesExpiry } =
|
||||
suitInvigorationUpgradeData(item);
|
||||
document.getElementById("dv-invigoration-offensive").value = OffensiveUpgrade;
|
||||
document.getElementById("dv-invigoration-defensive").value = DefensiveUpgrade;
|
||||
document.getElementById("dv-invigoration-expiry").value = UpgradesExpiry;
|
||||
|
||||
{
|
||||
document.getElementById("loadout-card").classList.remove("d-none");
|
||||
const maxModConfigNum = Math.min(2 + (item.ModSlotPurchases ?? 0), 5);
|
||||
|
||||
const configs = item.Configs ?? [];
|
||||
|
||||
const loadoutTabs = document.getElementById("loadoutTabs");
|
||||
const loadoutTabsContent = document.getElementById("loadoutTabsContent");
|
||||
loadoutTabs.innerHTML = "";
|
||||
loadoutTabsContent.innerHTML = "";
|
||||
for (let i = 0; i <= maxModConfigNum; i++) {
|
||||
const config = configs[i] ?? {};
|
||||
|
||||
{
|
||||
const li = document.createElement("li");
|
||||
li.classList.add("nav-item");
|
||||
|
||||
const button = document.createElement("button");
|
||||
button.classList.add("nav-link");
|
||||
if (i === 0) button.classList.add("active");
|
||||
button.id = `config${i}-tab`;
|
||||
button.setAttribute("data-bs-toggle", "tab");
|
||||
button.setAttribute("data-bs-target", `#config${i}`);
|
||||
button.innerHTML = config.Name?.trim() || String.fromCharCode(65 + i);
|
||||
|
||||
li.appendChild(button);
|
||||
loadoutTabs.appendChild(li);
|
||||
}
|
||||
|
||||
{
|
||||
const tabDiv = document.createElement("div");
|
||||
tabDiv.classList = "tab-pane";
|
||||
if (i === 0) tabDiv.classList.add("show", "active");
|
||||
|
||||
tabDiv.id = `config${i}`;
|
||||
|
||||
{
|
||||
const abilityOverrideForm = document.createElement("form");
|
||||
abilityOverrideForm.classList = "form-group mt-2";
|
||||
abilityOverrideForm.setAttribute(
|
||||
"onsubmit",
|
||||
`handleAbilityOverride(event, ${i});return false;`
|
||||
);
|
||||
|
||||
const abilityOverrideFormLabel = document.createElement("label");
|
||||
abilityOverrideFormLabel.setAttribute("data-loc", "abilityOverride_label");
|
||||
abilityOverrideFormLabel.innerHTML = loc("abilityOverride_label");
|
||||
abilityOverrideFormLabel.classList = "form-label";
|
||||
abilityOverrideFormLabel.setAttribute("for", "abilityOverride-ability");
|
||||
abilityOverrideForm.appendChild(abilityOverrideFormLabel);
|
||||
|
||||
const abilityOverrideInputGroup = document.createElement("div");
|
||||
abilityOverrideInputGroup.classList = "input-group";
|
||||
abilityOverrideForm.appendChild(abilityOverrideInputGroup);
|
||||
|
||||
const abilityOverrideInput = document.createElement("input");
|
||||
abilityOverrideInput.id = "abilityOverride-ability";
|
||||
abilityOverrideInput.classList = "form-control";
|
||||
abilityOverrideInput.setAttribute("list", "datalist-Abilities");
|
||||
if (config.AbilityOverride) {
|
||||
const datalist = document.getElementById("datalist-Abilities");
|
||||
const options = Array.from(datalist.options);
|
||||
abilityOverrideInput.value = options.find(
|
||||
option =>
|
||||
config.AbilityOverride.Ability == option.getAttribute("data-key")
|
||||
).value;
|
||||
}
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideInput);
|
||||
|
||||
const abilityOverrideOnSlot = document.createElement("span");
|
||||
abilityOverrideOnSlot.classList = "input-group-text";
|
||||
abilityOverrideOnSlot.setAttribute("data-loc", "abilityOverride_onSlot");
|
||||
abilityOverrideOnSlot.innerHTML = loc("abilityOverride_onSlot");
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideOnSlot);
|
||||
|
||||
const abilityOverrideSecondInput = document.createElement("input");
|
||||
abilityOverrideSecondInput.id = "abilityOverride-ability-index";
|
||||
abilityOverrideSecondInput.classList = "form-control";
|
||||
abilityOverrideSecondInput.setAttribute("type", "number");
|
||||
abilityOverrideSecondInput.setAttribute("min", "0");
|
||||
abilityOverrideSecondInput.setAttribute("max", "3");
|
||||
if (config.AbilityOverride)
|
||||
abilityOverrideSecondInput.value = config.AbilityOverride.Index;
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideSecondInput);
|
||||
|
||||
const abilityOverrideSetButton = document.createElement("button");
|
||||
abilityOverrideSetButton.classList = "btn btn-primary";
|
||||
abilityOverrideSetButton.setAttribute("type", "submit");
|
||||
abilityOverrideSetButton.setAttribute("value", "set");
|
||||
abilityOverrideSetButton.setAttribute("data-loc", "general_setButton");
|
||||
abilityOverrideSetButton.innerHTML = loc("general_setButton");
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideSetButton);
|
||||
|
||||
const abilityOverrideRemoveButton = document.createElement("button");
|
||||
abilityOverrideRemoveButton.classList = "btn btn-danger";
|
||||
abilityOverrideRemoveButton.setAttribute("type", "submit");
|
||||
abilityOverrideRemoveButton.setAttribute("value", "remove");
|
||||
abilityOverrideRemoveButton.setAttribute("data-loc", "code_remove");
|
||||
abilityOverrideRemoveButton.innerHTML = loc("code_remove");
|
||||
abilityOverrideInputGroup.appendChild(abilityOverrideRemoveButton);
|
||||
|
||||
abilityOverrideForm.appendChild(abilityOverrideInputGroup);
|
||||
|
||||
tabDiv.appendChild(abilityOverrideForm);
|
||||
}
|
||||
|
||||
loadoutTabsContent.appendChild(tabDiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (["LongGuns", "Pistols", "Melee", "SpaceGuns", "SpaceMelee"].includes(category)) {
|
||||
document.getElementById("valenceBonus-card").classList.remove("d-none");
|
||||
document.getElementById("valenceBonus-innateDamage").value = "";
|
||||
@ -1233,6 +1365,35 @@ function updateInventory() {
|
||||
document.getElementById("valenceBonus-procent").value = Math.round(buffValue * 1000) / 10;
|
||||
}
|
||||
}
|
||||
if (modularWeapons.includes(item.ItemType)) {
|
||||
document.getElementById("modularParts-card").classList.remove("d-none");
|
||||
const form = document.getElementById("modularParts-form");
|
||||
form.innerHTML = "";
|
||||
const requiredParts = getRequiredParts(category, item.ItemType);
|
||||
|
||||
requiredParts.forEach(modularPart => {
|
||||
const input = document.createElement("input");
|
||||
input.classList.add("form-control");
|
||||
input.id = "detailedView-modularPart-" + modularPart;
|
||||
input.setAttribute("list", "datalist-ModularParts-" + modularPart);
|
||||
|
||||
const datalist = document.getElementById("datalist-ModularParts-" + modularPart);
|
||||
const options = Array.from(datalist.options);
|
||||
|
||||
input.value =
|
||||
options.find(option => item.ModularParts.includes(option.getAttribute("data-key")))
|
||||
?.value || "";
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
const changeButton = document.createElement("button");
|
||||
changeButton.classList.add("btn");
|
||||
changeButton.classList.add("btn-primary");
|
||||
changeButton.type = "submit";
|
||||
changeButton.setAttribute("data-loc", "cheats_changeButton");
|
||||
changeButton.innerHTML = loc("cheats_changeButton");
|
||||
form.appendChild(changeButton);
|
||||
}
|
||||
} else {
|
||||
single.loadRoute("/webui/inventory");
|
||||
}
|
||||
@ -1332,47 +1493,41 @@ function doAcquireEquipment(category) {
|
||||
});
|
||||
}
|
||||
|
||||
function doAcquireModularEquipment(category, WeaponType) {
|
||||
let requiredParts;
|
||||
let Parts = [];
|
||||
function getRequiredParts(category, WeaponType) {
|
||||
switch (category) {
|
||||
case "HoverBoards":
|
||||
WeaponType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
|
||||
requiredParts = ["HB_DECK", "HB_ENGINE", "HB_FRONT", "HB_JET"];
|
||||
break;
|
||||
case "Hoverboards":
|
||||
return ["HB_DECK", "HB_ENGINE", "HB_FRONT", "HB_JET"];
|
||||
|
||||
case "OperatorAmps":
|
||||
requiredParts = ["AMP_OCULUS", "AMP_CORE", "AMP_BRACE"];
|
||||
break;
|
||||
return ["AMP_OCULUS", "AMP_CORE", "AMP_BRACE"];
|
||||
|
||||
case "Melee":
|
||||
requiredParts = ["BLADE", "HILT", "HILT_WEIGHT"];
|
||||
break;
|
||||
return ["BLADE", "HILT", "HILT_WEIGHT"];
|
||||
|
||||
case "LongGuns":
|
||||
requiredParts = ["GUN_BARREL", "GUN_PRIMARY_HANDLE", "GUN_CLIP"];
|
||||
break;
|
||||
return ["GUN_BARREL", "GUN_PRIMARY_HANDLE", "GUN_CLIP"];
|
||||
|
||||
case "Pistols":
|
||||
requiredParts = ["GUN_BARREL", "GUN_SECONDARY_HANDLE", "GUN_CLIP"];
|
||||
break;
|
||||
return ["GUN_BARREL", "GUN_SECONDARY_HANDLE", "GUN_CLIP"];
|
||||
|
||||
case "MoaPets":
|
||||
if (WeaponType == "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit") {
|
||||
requiredParts = ["MOA_ENGINE", "MOA_PAYLOAD", "MOA_HEAD", "MOA_LEG"];
|
||||
} else {
|
||||
requiredParts = ["ZANUKA_BODY", "ZANUKA_HEAD", "ZANUKA_LEG", "ZANUKA_TAIL"];
|
||||
}
|
||||
break;
|
||||
case "KubrowPets":
|
||||
if (
|
||||
[
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit"
|
||||
].includes(WeaponType)
|
||||
) {
|
||||
requiredParts = ["CATBROW_ANTIGEN", "CATBROW_MUTAGEN"];
|
||||
} else {
|
||||
requiredParts = ["KUBROW_ANTIGEN", "KUBROW_MUTAGEN"];
|
||||
}
|
||||
break;
|
||||
return WeaponType === "/Lotus/Types/Friendly/Pets/MoaPets/MoaPetPowerSuit"
|
||||
? ["MOA_ENGINE", "MOA_PAYLOAD", "MOA_HEAD", "MOA_LEG"]
|
||||
: ["ZANUKA_BODY", "ZANUKA_HEAD", "ZANUKA_LEG", "ZANUKA_TAIL"];
|
||||
|
||||
case "KubrowPets": {
|
||||
return WeaponType.endsWith("InfestedCatbrowPetPowerSuit")
|
||||
? ["CATBROW_ANTIGEN", "CATBROW_MUTAGEN"]
|
||||
: ["KUBROW_ANTIGEN", "KUBROW_MUTAGEN"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doAcquireModularEquipment(category, WeaponType) {
|
||||
if (category === "Hoverboards") WeaponType = "/Lotus/Types/Vehicles/Hoverboard/HoverboardSuit";
|
||||
const requiredParts = getRequiredParts(category, WeaponType);
|
||||
let Parts = [];
|
||||
|
||||
requiredParts.forEach(part => {
|
||||
const partName = getKey(document.getElementById("acquire-type-" + category + "-" + part));
|
||||
if (partName) {
|
||||
@ -1489,19 +1644,22 @@ function doAcquireEvolution() {
|
||||
setEvolutionProgress([{ ItemType: uniqueName, Rank: permanentEvolutionWeapons.has(uniqueName) ? 0 : 1 }]);
|
||||
}
|
||||
|
||||
$("input[list]").on("input", function () {
|
||||
$(document).on("input", "input[list]", function () {
|
||||
$(this).removeClass("is-invalid");
|
||||
});
|
||||
|
||||
function dispatchAddItemsRequestsBatch(requests) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addItems?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
return new Promise(resolve => {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/addItems?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1522,7 +1680,7 @@ function addMissingEquipment(categories) {
|
||||
});
|
||||
});
|
||||
if (requests.length != 0 && window.confirm(loc("code_addItemsConfirm").split("|COUNT|").join(requests.length))) {
|
||||
dispatchAddItemsRequestsBatch(requests);
|
||||
return dispatchAddItemsRequestsBatch(requests);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1538,7 +1696,7 @@ function addMissingEvolutionProgress() {
|
||||
requests.push({ ItemType: uniqueName, Rank: permanentEvolutionWeapons.has(uniqueName) ? 0 : 1 });
|
||||
});
|
||||
if (requests.length != 0 && window.confirm(loc("code_addItemsConfirm").split("|COUNT|").join(requests.length))) {
|
||||
setEvolutionProgress(requests);
|
||||
return setEvolutionProgress(requests);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1706,7 +1864,7 @@ function disposeOfGear(category, oid) {
|
||||
];
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
@ -1728,7 +1886,7 @@ function disposeOfItems(category, type, count) {
|
||||
];
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
@ -1765,14 +1923,17 @@ function maturePet(oid, revert) {
|
||||
}
|
||||
|
||||
function setEvolutionProgress(requests) {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/setEvolutionProgress?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
return new Promise(resolve => {
|
||||
revalidateAuthz().then(() => {
|
||||
const req = $.post({
|
||||
url: "/custom/setEvolutionProgress?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(requests)
|
||||
});
|
||||
req.done(() => {
|
||||
updateInventory();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1938,7 +2099,13 @@ for (const id of uiConfigs) {
|
||||
if (elm.tagName == "SELECT") {
|
||||
elm.onchange = function () {
|
||||
let value = this.value;
|
||||
if (!isNaN(parseInt(value))) {
|
||||
if (value == "true") {
|
||||
value = true;
|
||||
} else if (value == "false") {
|
||||
value = false;
|
||||
} else if (value == "null") {
|
||||
value = null;
|
||||
} else if (!isNaN(parseInt(value))) {
|
||||
value = parseInt(value);
|
||||
}
|
||||
$.post({
|
||||
@ -2040,6 +2207,10 @@ single.getRoute("/webui/cheats").on("beforeload", function () {
|
||||
})
|
||||
.fail(res => {
|
||||
if (res.responseText == "Log-in expired") {
|
||||
if (ws_is_open && !auth_pending) {
|
||||
console.warn("Credentials invalidated but the server didn't let us know");
|
||||
sendAuth();
|
||||
}
|
||||
revalidateAuthz().then(() => {
|
||||
if (single.getCurrentPath() == "/webui/cheats") {
|
||||
single.loadRoute("/webui/cheats");
|
||||
@ -2162,7 +2333,7 @@ function doRemoveUnrankedMods() {
|
||||
req.done(inventory => {
|
||||
window.itemListPromise.then(itemMap => {
|
||||
$.post({
|
||||
url: "/api/sell.php?" + window.authz,
|
||||
url: "/api/sell.php?" + window.authz + "&wsid=" + wsid,
|
||||
contentType: "text/plain",
|
||||
data: JSON.stringify({
|
||||
SellCurrency: "SC_RegularCredits",
|
||||
@ -2195,7 +2366,11 @@ single.getRoute("#detailedView-route").on("beforeload", function () {
|
||||
document.getElementById("detailedView-loading").classList.remove("d-none");
|
||||
document.getElementById("detailedView-title").textContent = "";
|
||||
document.querySelector("#detailedView-route .text-body-secondary").textContent = "";
|
||||
document.getElementById("loadout-card").classList.add("d-none");
|
||||
document.getElementById("archonShards-card").classList.add("d-none");
|
||||
document.getElementById("edit-suit-invigorations-card").classList.add("d-none");
|
||||
document.getElementById("modularParts-card").classList.add("d-none");
|
||||
document.getElementById("modularParts-form").innerHTML = "";
|
||||
document.getElementById("valenceBonus-card").classList.add("d-none");
|
||||
if (window.didInitialInventoryUpdate) {
|
||||
updateInventory();
|
||||
@ -2353,22 +2528,10 @@ function handleModularSelection(category) {
|
||||
modularFieldsZanuka.style.display = "none";
|
||||
}
|
||||
} else if (inventoryCategory === "KubrowPets") {
|
||||
if (
|
||||
[
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VulpineInfestedCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/HornedInfestedCatbrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/ArmoredInfestedCatbrowPetPowerSuit"
|
||||
].includes(key)
|
||||
) {
|
||||
if (key.endsWith("InfestedCatbrowPetPowerSuit")) {
|
||||
modularFieldsCatbrow.style.display = "";
|
||||
modularFieldsKubrow.style.display = "none";
|
||||
} else if (
|
||||
[
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/VizierPredatorKubrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/PharaohPredatorKubrowPetPowerSuit",
|
||||
"/Lotus/Types/Friendly/Pets/CreaturePets/MedjayPredatorKubrowPetPowerSuit"
|
||||
].includes(key)
|
||||
) {
|
||||
} else if (key.endsWith("PredatorKubrowPetPowerSuit")) {
|
||||
modularFieldsCatbrow.style.display = "none";
|
||||
modularFieldsKubrow.style.display = "";
|
||||
} else {
|
||||
@ -2469,9 +2632,17 @@ function formatDatetime(fmt, date) {
|
||||
const calls_in_flight = new Set();
|
||||
|
||||
async function debounce(func, ...args) {
|
||||
calls_in_flight.add(func);
|
||||
await func(...args);
|
||||
calls_in_flight.delete(func);
|
||||
if (!func.name) {
|
||||
throw new Error(`cannot debounce anonymous functions`);
|
||||
}
|
||||
const callid = JSON.stringify({ func: func.name, args });
|
||||
if (!calls_in_flight.has(callid)) {
|
||||
calls_in_flight.add(callid);
|
||||
await func(...args);
|
||||
calls_in_flight.delete(callid);
|
||||
} else {
|
||||
console.log("debouncing", callid);
|
||||
}
|
||||
}
|
||||
|
||||
async function doMaxPlexus() {
|
||||
@ -2807,3 +2978,133 @@ async function markAllAsRead() {
|
||||
}
|
||||
toast(loc(any ? "code_succRelog" : "code_nothingToDo"));
|
||||
}
|
||||
|
||||
function handleModularPartsChange(event) {
|
||||
event.preventDefault();
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const form = document.getElementById("modularParts-form");
|
||||
const inputs = form.querySelectorAll("input");
|
||||
const modularParts = [];
|
||||
inputs.forEach(input => {
|
||||
const key = getKey(input);
|
||||
if (!key) {
|
||||
input.classList.add("is-invalid");
|
||||
} else {
|
||||
modularParts.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
if (inputs.length == modularParts.length) {
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/changeModularParts?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
category: urlParams.get("productCategory"),
|
||||
oid: urlParams.get("itemId"),
|
||||
modularParts
|
||||
})
|
||||
}).then(function () {
|
||||
toast(loc("code_succChange"));
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
function suitInvigorationUpgradeData(suitData) {
|
||||
let expiryDate = "";
|
||||
if (suitData.UpgradesExpiry) {
|
||||
if (suitData.UpgradesExpiry.$date) {
|
||||
expiryDate = new Date(parseInt(suitData.UpgradesExpiry.$date.$numberLong));
|
||||
} else if (typeof suitData.UpgradesExpiry === "number") {
|
||||
expiryDate = new Date(suitData.UpgradesExpiry);
|
||||
} else if (suitData.UpgradesExpiry instanceof Date) {
|
||||
expiryDate = suitData.UpgradesExpiry;
|
||||
}
|
||||
if (expiryDate && !isNaN(expiryDate.getTime())) {
|
||||
const year = expiryDate.getFullYear();
|
||||
const month = String(expiryDate.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(expiryDate.getDate()).padStart(2, "0");
|
||||
const hours = String(expiryDate.getHours()).padStart(2, "0");
|
||||
const minutes = String(expiryDate.getMinutes()).padStart(2, "0");
|
||||
expiryDate = `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
} else {
|
||||
expiryDate = "";
|
||||
}
|
||||
}
|
||||
return {
|
||||
oid: suitData.ItemId.$oid,
|
||||
OffensiveUpgrade: suitData.OffensiveUpgrade || "",
|
||||
DefensiveUpgrade: suitData.DefensiveUpgrade || "",
|
||||
UpgradesExpiry: expiryDate
|
||||
};
|
||||
}
|
||||
|
||||
function submitSuitInvigorationUpgrade(event) {
|
||||
event.preventDefault();
|
||||
const oid = new URLSearchParams(window.location.search).get("itemId");
|
||||
const offensiveUpgrade = document.getElementById("dv-invigoration-offensive").value;
|
||||
const defensiveUpgrade = document.getElementById("dv-invigoration-defensive").value;
|
||||
const expiry = document.getElementById("dv-invigoration-expiry").value;
|
||||
|
||||
if (!offensiveUpgrade || !defensiveUpgrade) {
|
||||
alert(loc("code_requiredInvigorationUpgrade"));
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
OffensiveUpgrade: offensiveUpgrade,
|
||||
DefensiveUpgrade: defensiveUpgrade
|
||||
};
|
||||
|
||||
if (expiry) {
|
||||
data.UpgradesExpiry = new Date(expiry).getTime();
|
||||
}
|
||||
|
||||
editSuitInvigorationUpgrade(oid, data);
|
||||
}
|
||||
|
||||
function clearSuitInvigorationUpgrades() {
|
||||
editSuitInvigorationUpgrade(new URLSearchParams(window.location.search).get("itemId"), null);
|
||||
}
|
||||
|
||||
async function editSuitInvigorationUpgrade(oid, data) {
|
||||
/* data?: {
|
||||
DefensiveUpgrade: string;
|
||||
OffensiveUpgrade: string;
|
||||
UpgradesExpiry?: number;
|
||||
}*/
|
||||
$.post({
|
||||
url: "/custom/editSuitInvigorationUpgrade?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({ oid, data })
|
||||
}).done(function () {
|
||||
updateInventory();
|
||||
});
|
||||
}
|
||||
|
||||
function handleAbilityOverride(event, configIndex) {
|
||||
event.preventDefault();
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const action = event.submitter.value;
|
||||
const Ability = getKey(document.getElementById("abilityOverride-ability"));
|
||||
const Index = document.getElementById("abilityOverride-ability-index").value;
|
||||
revalidateAuthz().then(() => {
|
||||
$.post({
|
||||
url: "/custom/abilityOverride?" + window.authz,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
category: urlParams.get("productCategory"),
|
||||
oid: urlParams.get("itemId"),
|
||||
configIndex,
|
||||
action,
|
||||
AbilityOverride: {
|
||||
Ability,
|
||||
Index
|
||||
}
|
||||
})
|
||||
}).done(function () {
|
||||
updateInventory();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
// German translation by Animan8000
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
|
||||
general_inventoryUpdateNote: `Hinweis: Um Änderungen im Spiel zu sehen, musst du dein Inventar neu synchronisieren, z. B. mit dem /sync Befehl des Bootstrappers, durch Besuch eines Dojo/Relais oder durch erneutes Einloggen.`,
|
||||
general_addButton: `Hinzufügen`,
|
||||
general_setButton: `[UNTRANSLATED] Set`,
|
||||
general_setButton: `Festlegen`,
|
||||
general_none: `Keines`,
|
||||
general_bulkActions: `Massenaktionen`,
|
||||
general_loading: `[UNTRANSLATED] Loading...`,
|
||||
general_loading: `Lädt...`,
|
||||
|
||||
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
|
||||
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
|
||||
code_loginFail: `Anmeldung fehlgeschlagen. Bitte überprüfe deine Angaben.`,
|
||||
code_regFail: `Registrierung fehlgeschlagen. Account existiert bereits?`,
|
||||
code_changeNameConfirm: `In welchen Namen möchtest du deinen Account umbenennen?`,
|
||||
code_deleteAccountConfirm: `Bist du sicher, dass du deinen Account |DISPLAYNAME| (|EMAIL|) löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.`,
|
||||
code_archgun: `Arch-Gewehr`,
|
||||
@ -45,8 +46,8 @@ dict = {
|
||||
code_focusUnlocked: `|COUNT| neue Fokus-Schulen freigeschaltet! Ein Inventar-Update wird benötigt, damit die Änderungen im Spiel sichtbar werden. Die Sternenkarte zu besuchen, sollte der einfachste Weg sein, dies auszulösen.`,
|
||||
code_addModsConfirm: `Bist du sicher, dass du |COUNT| Mods zu deinem Account hinzufügen möchtest?`,
|
||||
code_succImport: `Erfolgreich importiert.`,
|
||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
||||
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`,
|
||||
code_zanuka: `Jagdhund`,
|
||||
@ -61,6 +62,8 @@ dict = {
|
||||
code_pigment: `Pigment`,
|
||||
code_mature: `Für den Kampf auswachsen lassen`,
|
||||
code_unmature: `Genetisches Altern zurücksetzen`,
|
||||
code_succChange: `Erfolgreich geändert.`,
|
||||
code_requiredInvigorationUpgrade: `Du musst sowohl ein offensives & defensives Upgrade auswählen.`,
|
||||
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`,
|
||||
@ -90,7 +93,7 @@ dict = {
|
||||
inventory_moaPets: `Moas`,
|
||||
inventory_kubrowPets: `Bestien`,
|
||||
inventory_evolutionProgress: `Incarnon-Entwicklungsfortschritte`,
|
||||
inventory_Boosters: `[UNTRANSLATED] Boosters`,
|
||||
inventory_Boosters: `Booster`,
|
||||
inventory_bulkAddSuits: `Fehlende Warframes hinzufügen`,
|
||||
inventory_bulkAddWeapons: `Fehlende Waffen hinzufügen`,
|
||||
inventory_bulkAddSpaceSuits: `Fehlende Archwings hinzufügen`,
|
||||
@ -105,7 +108,7 @@ 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_maxPlexus: `[UNTRANSLATED] Max Rank Plexus`,
|
||||
inventory_maxPlexus: `Plexus auf Max. Rang`,
|
||||
|
||||
quests_list: `Quests`,
|
||||
quests_completeAll: `Alle Quests abschließen`,
|
||||
@ -122,14 +125,46 @@ dict = {
|
||||
detailedView_archonShardsDescription: `Du kannst diese unbegrenzten Slots nutzen, um eine Vielzahl von Verbesserungen anzuwenden.`,
|
||||
detailedView_archonShardsDescription2: `Hinweis: Jede Archon-Scherbe benötigt beim Laden etwas Zeit, um angewendet zu werden.`,
|
||||
detailedView_valenceBonusLabel: `Valenz-Bonus`,
|
||||
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can set or remove the Valence Bonus from your weapon.`,
|
||||
detailedView_valenceBonusDescription: `Du kannst den Valenz-Bonus deiner Waffe festlegen oder entfernen.`,
|
||||
detailedView_modularPartsLabel: `Modulare Teile ändern`,
|
||||
detailedView_suitInvigorationLabel: `Warframe-Kräftigung`,
|
||||
detailedView_loadoutLabel: `Loadouts`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% Fähigkeitsstärke`,
|
||||
invigorations_offensive_AbilityRange: `+100% Fähigkeitsreichweite`,
|
||||
invigorations_offensive_AbilityDuration: `+100% Fähigkeitsdauer`,
|
||||
invigorations_offensive_MeleeDamage: `+250% Nahkampfschaden`,
|
||||
invigorations_offensive_PrimaryDamage: `+250% Primärwaffen Schaden`,
|
||||
invigorations_offensive_SecondaryDamage: `+250% Sekundärwaffen Schaden`,
|
||||
invigorations_offensive_PrimaryCritChance: `+200% Primärwaffen Krit. Chance`,
|
||||
invigorations_offensive_SecondaryCritChance: `+200% Sekundärwaffen Krit. Chance`,
|
||||
invigorations_offensive_MeleeCritChance: `+200% Nahkampfwaffen Krit. Chance`,
|
||||
|
||||
invigorations_utility_AbilityEfficiency: `+75% Fähigkeitseffizienz`,
|
||||
invigorations_utility_SprintSpeed: `+75% Sprintgeschwindigkeit`,
|
||||
invigorations_utility_ParkourVelocity: `+75% Parkourgeschwindigkeit`,
|
||||
invigorations_utility_HealthMax: `+1000 Gesundheit`,
|
||||
invigorations_utility_EnergyMax: `+200% Max. Energie`,
|
||||
invigorations_utility_StatusImmune: `Immun gegen Statuseffekte`,
|
||||
invigorations_utility_ReloadSpeed: `+75% Nachladegeschwindigkeit`,
|
||||
invigorations_utility_HealthRegen: `+25 Gesundheitsregeneration pro Sekunde`,
|
||||
invigorations_utility_ArmorMax: `+1000 Rüstung`,
|
||||
invigorations_utility_Jumps: `+5 Sprung-Zurücksetzungen`,
|
||||
invigorations_utility_EnergyRegen: `+2 Energieregeneration pro Sekunde`,
|
||||
|
||||
invigorations_offensiveLabel: `Offensives Upgrade`,
|
||||
invigorations_defensiveLabel: `Defensives Upgrade`,
|
||||
invigorations_expiryLabel: `Upgrades Ablaufdatum (optional)`,
|
||||
|
||||
abilityOverride_label: `Fähigkeitsüberschreibung`,
|
||||
abilityOverride_onSlot: `auf Slot`,
|
||||
|
||||
mods_addRiven: `Riven hinzufügen`,
|
||||
mods_fingerprint: `Fingerabdruck`,
|
||||
mods_fingerprintHelp: `Benötigst du Hilfe mit dem Fingerabdruck?`,
|
||||
mods_rivens: `Rivens`,
|
||||
mods_mods: `Mods`,
|
||||
mods_addMax: `[UNTRANSLATED] Add Maxed`,
|
||||
mods_addMax: `Max. hinzufügen`,
|
||||
mods_addMissingUnrankedMods: `Fehlende Mods ohne Rang hinzufügen`,
|
||||
mods_removeUnranked: `Mods ohne Rang entfernen`,
|
||||
mods_addMissingMaxRankMods: `Fehlende Mods mit Max. Rang hinzufügen`,
|
||||
@ -139,17 +174,17 @@ dict = {
|
||||
cheats_skipAllDialogue: `Alle Dialoge überspringen`,
|
||||
cheats_unlockAllScans: `Alle Scans freischalten`,
|
||||
cheats_unlockAllMissions: `Alle Missionen freischalten`,
|
||||
cheats_unlockAllMissions_ok: `[UNTRANSLATED] Success. Please note that you'll need to enter a dojo/relay or relog for the client to refresh the star chart.`,
|
||||
cheats_unlockAllMissions_ok: `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`,
|
||||
cheats_infiniteRegalAya: `Unendlich Reines Aya`,
|
||||
cheats_infiniteHelminthMaterials: `Unendlich Helminth-Materialien`,
|
||||
cheats_claimingBlueprintRefundsIngredients: `Fertige Blaupausen erstatten Ressourcen zurück`,
|
||||
cheats_dontSubtractPurchaseCreditCost: `[UNTRANSLATED] Don't Subtract Purchase Credit Cost`,
|
||||
cheats_dontSubtractPurchasePlatinumCost: `[UNTRANSLATED] Don't Subtract Purchase Platinum Cost`,
|
||||
cheats_dontSubtractPurchaseItemCost: `[UNTRANSLATED] Don't Subtract Purchase Item Cost`,
|
||||
cheats_dontSubtractPurchaseStandingCost: `[UNTRANSLATED] Don't Subtract Purchase Standing Cost`,
|
||||
cheats_dontSubtractPurchaseCreditCost: `Credits beim Kauf nicht verbrauchen`,
|
||||
cheats_dontSubtractPurchasePlatinumCost: `Platinum beim Kauf nicht verbrauchen`,
|
||||
cheats_dontSubtractPurchaseItemCost: `Gegenstände beim Kauf nicht verbrauchen`,
|
||||
cheats_dontSubtractPurchaseStandingCost: `Ansehen beim Kauf nicht verbrauchen`,
|
||||
cheats_dontSubtractVoidTraces: `Void-Spuren nicht verbrauchen`,
|
||||
cheats_dontSubtractConsumables: `Verbrauchsgegenstände (Ausrüstung) nicht verbrauchen`,
|
||||
cheats_unlockAllShipFeatures: `Alle Schiffs-Funktionen freischalten`,
|
||||
@ -169,11 +204,11 @@ dict = {
|
||||
cheats_noVendorPurchaseLimits: `Keine Kaufbeschränkungen bei Händlern`,
|
||||
cheats_noDeathMarks: `Keine Todesmarkierungen`,
|
||||
cheats_noKimCooldowns: `Keine Wartezeit bei KIM`,
|
||||
cheats_fullyStockedVendors: `[UNTRANSLATED] Fully Stocked Vendors`,
|
||||
cheats_baroAlwaysAvailable: `[UNTRANSLATED] Baro Always Available`,
|
||||
cheats_baroFullyStocked: `[UNTRANSLATED] Baro Fully Stocked`,
|
||||
cheats_fullyStockedVendors: `Händler haben volles Inventar`,
|
||||
cheats_baroAlwaysAvailable: `Baro immer im Relais verfügbar`,
|
||||
cheats_baroFullyStocked: `Baro hat volles Inventar`,
|
||||
cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
|
||||
cheats_unlockAllProfitTakerStages: `[UNTRANSLATED] Unlock All Profit Taker Stages`,
|
||||
cheats_unlockAllProfitTakerStages: `Alle Profiteintreiber-Phasen freischalten`,
|
||||
cheats_instantFinishRivenChallenge: `Riven-Mod Herausforderung sofort abschließen`,
|
||||
cheats_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`,
|
||||
cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`,
|
||||
@ -184,129 +219,131 @@ dict = {
|
||||
cheats_noDojoResearchCosts: `Keine Dojo-Forschungskosten`,
|
||||
cheats_noDojoResearchTime: `Keine Dojo-Forschungszeit`,
|
||||
cheats_fastClanAscension: `Schneller Clan-Aufstieg`,
|
||||
cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
|
||||
cheats_exceptionalRelicsAlwaysGiveBronzeReward: `[UNTRANSLATED] Exceptional Relics Always Give Bronze Reward`,
|
||||
cheats_flawlessRelicsAlwaysGiveSilverReward: `[UNTRANSLATED] Flawless Relics Always Give Silver Reward`,
|
||||
cheats_radiantRelicsAlwaysGiveGoldReward: `[UNTRANSLATED] Radiant Relics Always Give Gold Reward`,
|
||||
cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
|
||||
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
|
||||
cheats_missionsCanGiveAllRelics: `Alle Relikte als Missionsbelohnung möglich`,
|
||||
cheats_exceptionalRelicsAlwaysGiveBronzeReward: `Überragende Relikte geben immer Bronze-Belohnung`,
|
||||
cheats_flawlessRelicsAlwaysGiveSilverReward: `Makellose Relikte geben immer Silber-Belohnung`,
|
||||
cheats_radiantRelicsAlwaysGiveGoldReward: `Strahlende Relikte geben immer Gold-Belohnung`,
|
||||
cheats_unlockAllSimarisResearchEntries: `Alle Simaris-Forschungseinträge freischalten`,
|
||||
cheats_disableDailyTribute: `Täglicher Tribut deaktivieren`,
|
||||
cheats_spoofMasteryRank: `Gefälschter Meisterschaftsrang (-1 zum deaktivieren)`,
|
||||
cheats_relicRewardItemCountMultiplier: `[UNTRANSLATED] Relic Reward Item Count Multiplier`,
|
||||
cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
|
||||
cheats_save: `[UNTRANSLATED] Save`,
|
||||
cheats_relicRewardItemCountMultiplier: `Belohnungsmultiplikator für Relikte`,
|
||||
cheats_nightwaveStandingMultiplier: `Nightwave Ansehen Multiplikator`,
|
||||
cheats_save: `Speichern`,
|
||||
cheats_account: `Account`,
|
||||
cheats_unlockAllFocusSchools: `Alle Fokus-Schulen freischalten`,
|
||||
cheats_helminthUnlockAll: `Helminth vollständig aufleveln`,
|
||||
cheats_addMissingSubsumedAbilities: `[UNTRANSLATED] Add Missing Subsumed Abilities`,
|
||||
cheats_addMissingSubsumedAbilities: `Fehlende konsumierte Fähigkeiten hinzufügen`,
|
||||
cheats_intrinsicsUnlockAll: `Alle Inhärenzen auf Max. Rang`,
|
||||
cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
|
||||
cheats_changeButton: `Ändern`,
|
||||
cheats_none: `Keines`,
|
||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
||||
cheats_markAllAsRead: `Posteingang als gelesen markieren`,
|
||||
|
||||
worldState: `[UNTRANSLATED] World State`,
|
||||
worldState_creditBoost: `[UNTRANSLATED] Credit Boost`,
|
||||
worldState_affinityBoost: `[UNTRANSLATED] Affinity Boost`,
|
||||
worldState_resourceBoost: `[UNTRANSLATED] Resource Boost`,
|
||||
worldState_starDays: `[UNTRANSLATED] Star Days`,
|
||||
worldState_galleonOfGhouls: `[UNTRANSLATED] Galleon of Ghouls`,
|
||||
disabled: `[UNTRANSLATED] Disabled`,
|
||||
worldState_we1: `[UNTRANSLATED] Weekend 1`,
|
||||
worldState_we2: `[UNTRANSLATED] Weekend 2`,
|
||||
worldState_we3: `[UNTRANSLATED] Weekend 3`,
|
||||
worldState_eidolonOverride: `[UNTRANSLATED] Eidolon Override`,
|
||||
worldState_day: `[UNTRANSLATED] Day`,
|
||||
worldState_night: `[UNTRANSLATED] Night`,
|
||||
worldState_vallisOverride: `[UNTRANSLATED] Orb Vallis Override`,
|
||||
worldState_warm: `[UNTRANSLATED] Warm`,
|
||||
worldState_cold: `[UNTRANSLATED] Cold`,
|
||||
worldState_duviriOverride: `[UNTRANSLATED] Duviri Override`,
|
||||
worldState_joy: `[UNTRANSLATED] Joy`,
|
||||
worldState_anger: `[UNTRANSLATED] Anger`,
|
||||
worldState_envy: `[UNTRANSLATED] Envy`,
|
||||
worldState_sorrow: `[UNTRANSLATED] Sorrow`,
|
||||
worldState_fear: `[UNTRANSLATED] Fear`,
|
||||
worldState_nightwaveOverride: `[UNTRANSLATED] Nightwave Override`,
|
||||
worldState_RadioLegionIntermission13Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 9`,
|
||||
worldState_RadioLegionIntermission12Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 8`,
|
||||
worldState_RadioLegionIntermission11Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 7`,
|
||||
worldState_RadioLegionIntermission10Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 6`,
|
||||
worldState_RadioLegionIntermission9Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 5`,
|
||||
worldState_RadioLegionIntermission8Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 4`,
|
||||
worldState_RadioLegionIntermission7Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 3`,
|
||||
worldState_RadioLegionIntermission6Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 2`,
|
||||
worldState_RadioLegionIntermission5Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 1`,
|
||||
worldState_RadioLegionIntermission4Syndicate: `[UNTRANSLATED] Nora's Choice`,
|
||||
worldState_RadioLegionIntermission3Syndicate: `[UNTRANSLATED] Intermission III`,
|
||||
worldState_RadioLegion3Syndicate: `[UNTRANSLATED] Glassmaker`,
|
||||
worldState_RadioLegionIntermission2Syndicate: `[UNTRANSLATED] Intermission II`,
|
||||
worldState_RadioLegion2Syndicate: `[UNTRANSLATED] The Emissary`,
|
||||
worldState_RadioLegionIntermissionSyndicate: `[UNTRANSLATED] Intermission I`,
|
||||
worldState_RadioLegionSyndicate: `[UNTRANSLATED] The Wolf of Saturn Six`,
|
||||
worldState_fissures: `[UNTRANSLATED] Fissures`,
|
||||
normal: `[UNTRANSLATED] Normal`,
|
||||
worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`,
|
||||
worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`,
|
||||
worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`,
|
||||
worldState_darvoStockMultiplier: `[UNTRANSLATED] Darvo Stock Multiplier`,
|
||||
worldState_varziaFullyStocked: `[UNTRANSLATED] Varzia Fully Stocked`,
|
||||
worldState_varziaOverride: `[UNTRANSLATED] Varzia Rotation Override`,
|
||||
worldState: `Weltstatus`,
|
||||
worldState_creditBoost: `Event Booster: Credit`,
|
||||
worldState_affinityBoost: `Event Booster: Erfahrung`,
|
||||
worldState_resourceBoost: `Event Booster: Ressourcen`,
|
||||
worldState_tennoLiveRelay: `TennoLive Relais`,
|
||||
worldState_baroTennoConRelay: `Baros TennoCon Relais`,
|
||||
worldState_starDays: `Sternen-Tage`,
|
||||
worldState_galleonOfGhouls: `Galeone der Ghule`,
|
||||
enabled: `Aktiviert`,
|
||||
disabled: `Deaktiviert`,
|
||||
worldState_we1: `Wochenende 1`,
|
||||
worldState_we2: `Wochenende 2`,
|
||||
worldState_we3: `Wochenende 3`,
|
||||
worldState_eidolonOverride: `Eidolon-Überschreibung`,
|
||||
worldState_day: `Tag`,
|
||||
worldState_night: `Nacht`,
|
||||
worldState_vallisOverride: `Orbis-Tal-Überschreibung`,
|
||||
worldState_warm: `Warm`,
|
||||
worldState_cold: `Kalt`,
|
||||
worldState_duviriOverride: `Duviri-Überschreibung`,
|
||||
worldState_joy: `Freude`,
|
||||
worldState_anger: `Wut`,
|
||||
worldState_envy: `Neid`,
|
||||
worldState_sorrow: `Trauer`,
|
||||
worldState_fear: `Angst`,
|
||||
worldState_nightwaveOverride: `Nightwave-Überschreibung`,
|
||||
worldState_RadioLegionIntermission13Syndicate: `Noras Mix - Vol. 9`,
|
||||
worldState_RadioLegionIntermission12Syndicate: `Noras Mix - Vol. 8`,
|
||||
worldState_RadioLegionIntermission11Syndicate: `Noras Mix - Vol. 7`,
|
||||
worldState_RadioLegionIntermission10Syndicate: `Noras Mix - Vol. 6`,
|
||||
worldState_RadioLegionIntermission9Syndicate: `Noras Mix - Vol. 5`,
|
||||
worldState_RadioLegionIntermission8Syndicate: `Noras Mix - Vol. 4`,
|
||||
worldState_RadioLegionIntermission7Syndicate: `Noras Mix - Vol. 3`,
|
||||
worldState_RadioLegionIntermission6Syndicate: `Noras Mix - Vol. 2`,
|
||||
worldState_RadioLegionIntermission5Syndicate: `Noras Mix - Vol. 1`,
|
||||
worldState_RadioLegionIntermission4Syndicate: `Noras Wahl`,
|
||||
worldState_RadioLegionIntermission3Syndicate: `Sendepause III`,
|
||||
worldState_RadioLegion3Syndicate: `Der Glasmacher`,
|
||||
worldState_RadioLegionIntermission2Syndicate: `Sendepause II`,
|
||||
worldState_RadioLegion2Syndicate: `Der Botschafter`,
|
||||
worldState_RadioLegionIntermissionSyndicate: `Sendepause I`,
|
||||
worldState_RadioLegionSyndicate: `Der Wolf von Saturn Six`,
|
||||
worldState_fissures: `Void-Risse`,
|
||||
normal: `Normal`,
|
||||
worldState_allAtOnceNormal: `Alle gleichzeitig, Normal`,
|
||||
worldState_allAtOnceSteelPath: `Alle gleichzeitig, Stählerne Pfad`,
|
||||
worldState_theCircuitOverride: `Der Rundkurs-Überschreibung`,
|
||||
worldState_darvoStockMultiplier: `Darvo-Vorratsmultiplikator`,
|
||||
worldState_varziaFullyStocked: `Varzia hat volles Inventar`,
|
||||
worldState_varziaOverride: `Varzia-Angebotsüberschreibung`,
|
||||
|
||||
import_importNote: `Du kannst hier eine vollständige oder teilweise Inventarantwort (Client-Darstellung) einfügen. Alle Felder, die vom Importer unterstützt werden, <b>werden in deinem Account überschrieben</b>.`,
|
||||
import_submit: `Absenden`,
|
||||
import_samples: `[UNTRANSLATED] Samples:`,
|
||||
import_samples_maxFocus: `[UNTRANSLATED] All Focus Schools Maxed Out`,
|
||||
import_samples: `Beispiele:`,
|
||||
import_samples_maxFocus: `Alle Fokus-Schulen maximiert`,
|
||||
|
||||
upgrade_Equilibrium: `[UNTRANSLATED] +|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
|
||||
upgrade_MeleeCritDamage: `[UNTRANSLATED] +|VAL|% Melee Critical Damage`,
|
||||
upgrade_PrimaryStatusChance: `[UNTRANSLATED] +|VAL|% Primary Status Chance`,
|
||||
upgrade_SecondaryCritChance: `[UNTRANSLATED] +|VAL|% Secondary Critical Chance`,
|
||||
upgrade_WarframeAbilityDuration: `[UNTRANSLATED] +|VAL|% Ability Duration`,
|
||||
upgrade_WarframeAbilityStrength: `[UNTRANSLATED] +|VAL|% Ability Strength`,
|
||||
upgrade_WarframeArmourMax: `[UNTRANSLATED] +|VAL| Armor`,
|
||||
upgrade_WarframeBlastProc: `[UNTRANSLATED] +|VAL| Shields on kill with Blast Damage`,
|
||||
upgrade_WarframeCastingSpeed: `[UNTRANSLATED] +|VAL|% Casting Speed`,
|
||||
upgrade_WarframeCorrosiveDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Corrosion Status`,
|
||||
upgrade_WarframeCorrosiveStack: `[UNTRANSLATED] Increase max stacks of Corrosion Status by +|VAL|`,
|
||||
upgrade_WarframeCritDamageBoost: `[UNTRANSLATED] +|VAL|% Melee Critical Damage (Doubles over 500 Energy)`,
|
||||
upgrade_WarframeElectricDamage: `[UNTRANSLATED] +|VAL1|% Primary Electricity Damage (+|VAL2|% per additional Shard)`,
|
||||
upgrade_WarframeElectricDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Electricity Status`,
|
||||
upgrade_WarframeEnergyMax: `[UNTRANSLATED] +|VAL| Energy Max`,
|
||||
upgrade_WarframeGlobeEffectEnergy: `[UNTRANSLATED] +|VAL|% Energy Orb Effectiveness`,
|
||||
upgrade_WarframeGlobeEffectHealth: `[UNTRANSLATED] +|VAL|% Health Orb Effectiveness`,
|
||||
upgrade_WarframeHealthMax: `[UNTRANSLATED] +|VAL| Health`,
|
||||
upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
|
||||
upgrade_WarframeParkourVelocity: `[UNTRANSLATED] +|VAL|% Parkour Velocity`,
|
||||
upgrade_WarframeRadiationDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Radiation Status`,
|
||||
upgrade_WarframeRegen: `[UNTRANSLATED] +|VAL| Health Regen/s`,
|
||||
upgrade_WarframeShieldMax: `[UNTRANSLATED] +|VAL| Shield`,
|
||||
upgrade_WarframeStartingEnergy: `[UNTRANSLATED] +|VAL|% Energy on Spawn`,
|
||||
upgrade_WarframeToxinDamage: `[UNTRANSLATED] +|VAL|% Toxin Status Effect Damage`,
|
||||
upgrade_WarframeToxinHeal: `[UNTRANSLATED] +|VAL| Health on damaging enemies with Toxin Status`,
|
||||
upgrade_WeaponCritBoostFromHeat: `[UNTRANSLATED] +|VAL1|% Secondary Critical Chance per Heat-affected enemy killed (Max |VAL2|%)`,
|
||||
upgrade_AvatarAbilityRange: `[UNTRANSLATED] +7.5% Ability Range`,
|
||||
upgrade_AvatarAbilityEfficiency: `[UNTRANSLATED] +5% Ability Efficiency`,
|
||||
upgrade_AvatarEnergyRegen: `[UNTRANSLATED] +0.5 Energy Regen/s`,
|
||||
upgrade_AvatarEnemyRadar: `[UNTRANSLATED] +5m Enemy Radar`,
|
||||
upgrade_AvatarLootRadar: `[UNTRANSLATED] +7m Loot Radar`,
|
||||
upgrade_WeaponAmmoMax: `[UNTRANSLATED] +15% Ammo Max`,
|
||||
upgrade_EnemyArmorReductionAura: `[UNTRANSLATED] -3% Enemy Armor`,
|
||||
upgrade_OnExecutionAmmo: `[UNTRANSLATED] +100% Primary and Secondary Magazine Refill on Mercy`,
|
||||
upgrade_OnExecutionHealthDrop: `[UNTRANSLATED] +100% Health Orb Chance on Mercy`,
|
||||
upgrade_OnExecutionEnergyDrop: `[UNTRANSLATED] +50% Energy Orb Chance on Mercy`,
|
||||
upgrade_OnFailHackReset: `[UNTRANSLATED] +50% Hacking Retry Chance`,
|
||||
upgrade_DamageReductionOnHack: `[UNTRANSLATED] +75% Damage Reduction while Hacking`,
|
||||
upgrade_OnExecutionReviveCompanion: `[UNTRANSLATED] Mercy Kills reduce Companion Recovery by 15s`,
|
||||
upgrade_OnExecutionParkourSpeed: `[UNTRANSLATED] +60% Parkour Speed after a Mercy for 15s`,
|
||||
upgrade_AvatarTimeLimitIncrease: `[UNTRANSLATED] +8s to Hacking`,
|
||||
upgrade_ElectrifyOnHack: `[UNTRANSLATED] Shock enemies within 20m while Hacking`,
|
||||
upgrade_OnExecutionTerrify: `[UNTRANSLATED] +50% chance for enemies within 15m to cower in fear for 8 seconds on Mercy`,
|
||||
upgrade_OnHackLockers: `[UNTRANSLATED] Unlock 5 lockers within 20m after Hacking`,
|
||||
upgrade_OnExecutionBlind: `[UNTRANSLATED] Blind enemies within 18m on Mercy`,
|
||||
upgrade_OnExecutionDrainPower: `[UNTRANSLATED] Next ability cast gains +50% Ability Strength on Mercy`,
|
||||
upgrade_OnHackSprintSpeed: `[UNTRANSLATED] +75% Sprint Speed for 15s after Hacking`,
|
||||
upgrade_SwiftExecute: `[UNTRANSLATED] +50% Mercy Kill Speed`,
|
||||
upgrade_OnHackInvis: `[UNTRANSLATED] Invisible for 15 seconds after Hacking`,
|
||||
upgrade_Equilibrium: `+|VAL|% Energie bei Gesundheitskugeln, +|VAL|% Gesundheit bei Energiekugeln`,
|
||||
upgrade_MeleeCritDamage: `+|VAL|% Krit. Nahkampfschaden`,
|
||||
upgrade_PrimaryStatusChance: `+|VAL|% Primärwaffen Statuschance`,
|
||||
upgrade_SecondaryCritChance: `+|VAL|% Sekundärwaffen Krit. Chance`,
|
||||
upgrade_WarframeAbilityDuration: `+|VAL|% Fähigkeitsdauer`,
|
||||
upgrade_WarframeAbilityStrength: `+|VAL|% Fähigkeitsstärke`,
|
||||
upgrade_WarframeArmorMax: `+|VAL| Rüstung`,
|
||||
upgrade_WarframeBlastProc: `+|VAL| Schilde beim Töten eines Gegners mit Explosionsschaden`,
|
||||
upgrade_WarframeCastingSpeed: `+|VAL|% Aktivierungsgeschwindigkeit`,
|
||||
upgrade_WarframeCorrosiveDamageBoost: `+|VAL|% Fähigkeitsschaden auf Gegner, die von Korrosions-Status betroffen sind`,
|
||||
upgrade_WarframeCorrosiveStack: `Erhöhe maximale Stapelanzahl von Korrosions-Status um +|VAL|`,
|
||||
upgrade_WarframeCritDamageBoost: `+|VAL|% Krit. Nahkampfschaden (verdoppelt bei über 500 Energie)`,
|
||||
upgrade_WarframeElectricDamage: `+|VAL1|% Primärwaffen Elektrizitätsschaden (+|VAL2|% pro zusätzlicher Scherbe)`,
|
||||
upgrade_WarframeElectricDamageBoost: `+|VAL|% Fähigkeitsschaden auf Gegner, die von Elektrizitäts-Status betroffen sind`,
|
||||
upgrade_WarframeEnergyMax: `+|VAL| Max. Energie`,
|
||||
upgrade_WarframeGlobeEffectEnergy: `+|VAL|% Wirksamkeit bei Energiekugeln`,
|
||||
upgrade_WarframeGlobeEffectHealth: `+|VAL|% Wirksamkeit bei Gesundheitskugeln`,
|
||||
upgrade_WarframeHealthMax: `+|VAL| Gesundheit`,
|
||||
upgrade_WarframeHPBoostFromImpact: `+|VAL1| Gesundheit beim Töten eines Gegners mit Explosionsschaden (Max. |VAL2| Gesundheit)`,
|
||||
upgrade_WarframeParkourVelocity: `+|VAL|% Parkourgeschwindigkeit`,
|
||||
upgrade_WarframeRadiationDamageBoost: `+|VAL|% Fähigkeitsschaden auf Gegner, die von Strahlungs-Status betroffen sind`,
|
||||
upgrade_WarframeHealthRegen: `+|VAL| Gesundheitsregeneration pro Sekunde`,
|
||||
upgrade_WarframeShieldMax: `+|VAL| Schildkapazität`,
|
||||
upgrade_WarframeStartingEnergy: `+|VAL|% Max. Energie beim Spawnen`,
|
||||
upgrade_WarframeToxinDamage: `+|VAL|% Schaden durch Gift-Statuseffekte`,
|
||||
upgrade_WarframeToxinHeal: `+|VAL| Gesundheit beim Schaden an Gegnern mit Gift-Status`,
|
||||
upgrade_WeaponCritBoostFromHeat: `+|VAL1|% Sekundärwaffen Krit. Chance pro getötetem Gegner mit Hitze-Statuseffekt (Max. |VAL2|%)`,
|
||||
upgrade_AvatarAbilityRange: `+7.5% Fähigkeitsreichweite`,
|
||||
upgrade_AvatarAbilityEfficiency: `+5% Fähigkeitseffizienz`,
|
||||
upgrade_AvatarEnergyRegen: `+0.5 Energieregeneration pro Sekunde`,
|
||||
upgrade_AvatarEnemyRadar: `+5m Feindradar`,
|
||||
upgrade_AvatarLootRadar: `+7m Beuteradar`,
|
||||
upgrade_WeaponAmmoMax: `+15% Max. Munition`,
|
||||
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ß`,
|
||||
upgrade_OnFailHackReset: `+50% Chance, das Hacken bei Fehlschlag zu wiederholen`,
|
||||
upgrade_DamageReductionOnHack: `+75% Schadensreduktion beim Hacken`,
|
||||
upgrade_OnExecutionReviveCompanion: `Gnadenstoß-Kills verkürzen die Erholungszeit des Begleiters um 15s`,
|
||||
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 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ß`,
|
||||
upgrade_OnHackSprintSpeed: `+75% Sprintgeschwindigkeit für 15s nach dem Hacken`,
|
||||
upgrade_SwiftExecute: `+50% Gnadenstoßgeschwindigkeit`,
|
||||
upgrade_OnHackInvis: `+15s Unsichtbarkeit nach dem Hacken`,
|
||||
|
||||
damageType_Electricity: `Elektrizität`,
|
||||
damageType_Fire: `Hitze`,
|
||||
@ -316,8 +353,8 @@ dict = {
|
||||
damageType_Poison: `Gift`,
|
||||
damageType_Radiation: `Strahlung`,
|
||||
|
||||
theme_dark: `[UNTRANSLATED] Dark Theme`,
|
||||
theme_light: `[UNTRANSLATED] Light Theme`,
|
||||
theme_dark: `Dunkles Design`,
|
||||
theme_light: `Helles Design`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ dict = {
|
||||
general_inventoryUpdateNote: `Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
|
||||
general_addButton: `Add`,
|
||||
general_setButton: `Set`,
|
||||
general_none: `None`,
|
||||
general_bulkActions: `Bulk Actions`,
|
||||
general_loading: `Loading...`,
|
||||
|
||||
@ -60,6 +61,8 @@ dict = {
|
||||
code_pigment: `Pigment`,
|
||||
code_mature: `Mature for combat`,
|
||||
code_unmature: `Regress genetic aging`,
|
||||
code_succChange: `Successfully changed.`,
|
||||
code_requiredInvigorationUpgrade: `You must select both an offensive & defensive upgrade.`,
|
||||
login_description: `Login using your OpenWF account credentials (same as in-game when connecting to this server).`,
|
||||
login_emailLabel: `Email address`,
|
||||
login_passwordLabel: `Password`,
|
||||
@ -122,6 +125,38 @@ dict = {
|
||||
detailedView_archonShardsDescription2: `Note that each archon shard takes some time to be applied when loading in.`,
|
||||
detailedView_valenceBonusLabel: `Valence Bonus`,
|
||||
detailedView_valenceBonusDescription: `You can set or remove the Valence Bonus from your weapon.`,
|
||||
detailedView_modularPartsLabel: `Change Modular Parts`,
|
||||
detailedView_suitInvigorationLabel: `Warframe Invigoration`,
|
||||
detailedView_loadoutLabel: `Loadouts`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% Ability Strength`,
|
||||
invigorations_offensive_AbilityRange: `+100% Ability Range`,
|
||||
invigorations_offensive_AbilityDuration: `+100% Ability Duration`,
|
||||
invigorations_offensive_MeleeDamage: `+250% Melee Damage`,
|
||||
invigorations_offensive_PrimaryDamage: `+250% Primary Damage`,
|
||||
invigorations_offensive_SecondaryDamage: `+250% Secondary Damage`,
|
||||
invigorations_offensive_PrimaryCritChance: `+200% Primary Critical Chance`,
|
||||
invigorations_offensive_SecondaryCritChance: `+200% Secondary Critical Chance`,
|
||||
invigorations_offensive_MeleeCritChance: `+200% Melee Critical Chance`,
|
||||
|
||||
invigorations_utility_AbilityEfficiency: `+75% Ability Efficiency`,
|
||||
invigorations_utility_SprintSpeed: `+75% Sprint Speed`,
|
||||
invigorations_utility_ParkourVelocity: `+75% Parkour Velocity`,
|
||||
invigorations_utility_HealthMax: `+1000 Health`,
|
||||
invigorations_utility_EnergyMax: `+200% Energy Max`,
|
||||
invigorations_utility_StatusImmune: `Immune to Status Effects`,
|
||||
invigorations_utility_ReloadSpeed: `+75% Reload Speed`,
|
||||
invigorations_utility_HealthRegen: `+25 Health Regen/s`,
|
||||
invigorations_utility_ArmorMax: `+1000 Armor`,
|
||||
invigorations_utility_Jumps: `+5 Jump Resets`,
|
||||
invigorations_utility_EnergyRegen: `+2 Energy Regen/s`,
|
||||
|
||||
invigorations_offensiveLabel: `Offensive Upgrade`,
|
||||
invigorations_defensiveLabel: `Defensive Upgrade`,
|
||||
invigorations_expiryLabel: `Upgrades Expiry (optional)`,
|
||||
|
||||
abilityOverride_label: `Ability Override`,
|
||||
abilityOverride_onSlot: `on slot`,
|
||||
|
||||
mods_addRiven: `Add Riven`,
|
||||
mods_fingerprint: `Fingerprint`,
|
||||
@ -200,22 +235,24 @@ dict = {
|
||||
cheats_intrinsicsUnlockAll: `Max Rank All Intrinsics`,
|
||||
cheats_changeSupportedSyndicate: `Supported syndicate`,
|
||||
cheats_changeButton: `Change`,
|
||||
cheats_none: `None`,
|
||||
cheats_markAllAsRead: `Mark Inbox As Read`,
|
||||
|
||||
worldState: `World State`,
|
||||
worldState_creditBoost: `Credit Boost`,
|
||||
worldState_affinityBoost: `Affinity Boost`,
|
||||
worldState_resourceBoost: `Resource Boost`,
|
||||
worldState_tennoLiveRelay: `TennoLive Relay`,
|
||||
worldState_baroTennoConRelay: `Baro's TennoCon Relay`,
|
||||
worldState_starDays: `Star Days`,
|
||||
worldState_galleonOfGhouls: `Galleon of Ghouls`,
|
||||
enabled: `Enabled`,
|
||||
disabled: `Disabled`,
|
||||
worldState_we1: `Weekend 1`,
|
||||
worldState_we2: `Weekend 2`,
|
||||
worldState_we3: `Weekend 3`,
|
||||
worldState_eidolonOverride: `Eidolon Override`,
|
||||
worldState_day: `Day`,
|
||||
worldState_night: `Night`,
|
||||
worldState_eidolonOverride: `Eidolon/Deimos Override`,
|
||||
worldState_day: `Day/Fass`,
|
||||
worldState_night: `Night/Vome`,
|
||||
worldState_vallisOverride: `Orb Vallis Override`,
|
||||
worldState_warm: `Warm`,
|
||||
worldState_cold: `Cold`,
|
||||
@ -262,7 +299,7 @@ dict = {
|
||||
upgrade_SecondaryCritChance: `+|VAL|% Secondary Critical Chance`,
|
||||
upgrade_WarframeAbilityDuration: `+|VAL|% Ability Duration`,
|
||||
upgrade_WarframeAbilityStrength: `+|VAL|% Ability Strength`,
|
||||
upgrade_WarframeArmourMax: `+|VAL| Armor`,
|
||||
upgrade_WarframeArmorMax: `+|VAL| Armor`,
|
||||
upgrade_WarframeBlastProc: `+|VAL| Shields on kill with Blast Damage`,
|
||||
upgrade_WarframeCastingSpeed: `+|VAL|% Casting Speed`,
|
||||
upgrade_WarframeCorrosiveDamageBoost: `+|VAL|% Ability Damage on enemies affected by Corrosion Status`,
|
||||
@ -277,7 +314,7 @@ dict = {
|
||||
upgrade_WarframeHPBoostFromImpact: `+|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
|
||||
upgrade_WarframeParkourVelocity: `+|VAL|% Parkour Velocity`,
|
||||
upgrade_WarframeRadiationDamageBoost: `+|VAL|% Ability Damage on enemies affected by Radiation Status`,
|
||||
upgrade_WarframeRegen: `+|VAL| Health Regen/s`,
|
||||
upgrade_WarframeHealthRegen: `+|VAL| Health Regen/s`,
|
||||
upgrade_WarframeShieldMax: `+|VAL| Shield`,
|
||||
upgrade_WarframeStartingEnergy: `+|VAL|% Energy on Spawn`,
|
||||
upgrade_WarframeToxinDamage: `+|VAL|% Toxin Status Effect Damage`,
|
||||
|
@ -3,6 +3,7 @@ dict = {
|
||||
general_inventoryUpdateNote: `Para ver los cambios en el juego, necesitas volver a sincronizar tu inventario, por ejemplo, usando el comando /sync del bootstrapper, visitando un dojo o repetidor, o volviendo a iniciar sesión.`,
|
||||
general_addButton: `Agregar`,
|
||||
general_setButton: `Establecer`,
|
||||
general_none: `Ninguno`,
|
||||
general_bulkActions: `Acciones masivas`,
|
||||
general_loading: `Cargando...`,
|
||||
|
||||
@ -61,6 +62,8 @@ dict = {
|
||||
code_pigment: `Pigmento`,
|
||||
code_mature: `Listo para el combate`,
|
||||
code_unmature: `Regresar el envejecimiento genético`,
|
||||
code_succChange: `Cambiado correctamente`,
|
||||
code_requiredInvigorationUpgrade: `Debes seleccionar una mejora ofensiva y una defensiva.`,
|
||||
login_description: `Inicia sesión con las credenciales de tu cuenta OpenWF (las mismas que usas en el juego al conectarte a este servidor).`,
|
||||
login_emailLabel: `Dirección de correo electrónico`,
|
||||
login_passwordLabel: `Contraseña`,
|
||||
@ -123,6 +126,38 @@ dict = {
|
||||
detailedView_archonShardsDescription2: `Ten en cuenta que cada fragmento de archón tarda un poco en aplicarse al cargar`,
|
||||
detailedView_valenceBonusLabel: `Bônus de Valência`,
|
||||
detailedView_valenceBonusDescription: `Puedes establecer o quitar el bono de valencia de tu arma.`,
|
||||
detailedView_modularPartsLabel: `Cambiar partes modulares`,
|
||||
detailedView_suitInvigorationLabel: `Vigorización de Warframe`,
|
||||
detailedView_loadoutLabel: `Equipamientos`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% Fuerza de Habilidad`,
|
||||
invigorations_offensive_AbilityRange: `+100% Alcance de Habilidad`,
|
||||
invigorations_offensive_AbilityDuration: `+100% Duración de Habilidad`,
|
||||
invigorations_offensive_MeleeDamage: `+250% Daño Cuerpo a Cuerpo`,
|
||||
invigorations_offensive_PrimaryDamage: `+250% Daño de Arma Principal`,
|
||||
invigorations_offensive_SecondaryDamage: `+250% Daño de Arma Secundaria`,
|
||||
invigorations_offensive_PrimaryCritChance: `+200% Probabilidad Crítica de Arma Principal`,
|
||||
invigorations_offensive_SecondaryCritChance: `+200% Probabilidad Crítica de Arma Secundaria`,
|
||||
invigorations_offensive_MeleeCritChance: `+200% Probabilidad Crítica Cuerpo a Cuerpo`,
|
||||
|
||||
invigorations_utility_AbilityEfficiency: `+75% Eficiencia de Habilidad`,
|
||||
invigorations_utility_SprintSpeed: `+75% Velocidad de Sprint`,
|
||||
invigorations_utility_ParkourVelocity: `+75% Velocidad de Parkour`,
|
||||
invigorations_utility_HealthMax: `+1000 Salud Máx.`,
|
||||
invigorations_utility_EnergyMax: `+200% Energía Máx.`,
|
||||
invigorations_utility_StatusImmune: `Inmune a Efectos de Estado`,
|
||||
invigorations_utility_ReloadSpeed: `+75% Velocidad de Recarga`,
|
||||
invigorations_utility_HealthRegen: `+25 Regeneración de Salud/s`,
|
||||
invigorations_utility_ArmorMax: `+1000 Armadura Máx.`,
|
||||
invigorations_utility_Jumps: `+5 Restablecimientos de Salto`,
|
||||
invigorations_utility_EnergyRegen: `+2 Regeneración de Energía/s`,
|
||||
|
||||
invigorations_offensiveLabel: `Mejora Ofensiva`,
|
||||
invigorations_defensiveLabel: `Mejora Defensiva`,
|
||||
invigorations_expiryLabel: `Caducidad de Mejoras (opcional)`,
|
||||
|
||||
abilityOverride_label: `Anulación de Habilidad`,
|
||||
abilityOverride_onSlot: `en el espacio`,
|
||||
|
||||
mods_addRiven: `Agregar Agrietado`,
|
||||
mods_fingerprint: `Huella digital`,
|
||||
@ -201,15 +236,17 @@ dict = {
|
||||
cheats_intrinsicsUnlockAll: `Maximizar todos los intrínsecos`,
|
||||
cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
|
||||
cheats_changeButton: `Cambiar`,
|
||||
cheats_none: `Ninguno`,
|
||||
cheats_markAllAsRead: `Marcar bandeja de entrada como leída`,
|
||||
|
||||
worldState: `Estado del mundo`,
|
||||
worldState_creditBoost: `Potenciador de Créditos`,
|
||||
worldState_affinityBoost: `Potenciador de Afinidad`,
|
||||
worldState_resourceBoost: `Potenciador de Recursos`,
|
||||
worldState_tennoLiveRelay: `Repetidor de TennoLive`,
|
||||
worldState_baroTennoConRelay: `Repetidor de Baro de la TennoCon`,
|
||||
worldState_starDays: `Días estelares`,
|
||||
worldState_galleonOfGhouls: `Galeón de Gules`,
|
||||
enabled: `Activado`,
|
||||
disabled: `Desactivado`,
|
||||
worldState_we1: `Semana 1`,
|
||||
worldState_we2: `Semana 2`,
|
||||
@ -263,7 +300,7 @@ dict = {
|
||||
upgrade_SecondaryCritChance: `+|VAL|% de probabilidad crítica en armas secundarias`,
|
||||
upgrade_WarframeAbilityDuration: `+|VAL|% de duración de habilidades`,
|
||||
upgrade_WarframeAbilityStrength: `+|VAL|% de fuerza de habilidades`,
|
||||
upgrade_WarframeArmourMax: `+|VAL| de armadura`,
|
||||
upgrade_WarframeArmorMax: `+|VAL| de armadura`,
|
||||
upgrade_WarframeBlastProc: `+|VAL| de escudos al matar con daño de explosión`,
|
||||
upgrade_WarframeCastingSpeed: `+|VAL|% de velocidad de lanzamiento de habilidades`,
|
||||
upgrade_WarframeCorrosiveDamageBoost: `+|VAL|% de daño de habilidades a enemigos con estado corrosivo`,
|
||||
@ -278,7 +315,7 @@ dict = {
|
||||
upgrade_WarframeHPBoostFromImpact: `+|VAL1| de salud al eliminar con daño explosivo (máx. |VAL2| de salud)`,
|
||||
upgrade_WarframeParkourVelocity: `+|VAL|% de velocidad de parkour`,
|
||||
upgrade_WarframeRadiationDamageBoost: `+|VAL|% de daño de habilidades a enemigos con estado radiactivo`,
|
||||
upgrade_WarframeRegen: `+|VAL| de regeneración de salud por segundo`,
|
||||
upgrade_WarframeHealthRegen: `+|VAL| de regeneración de salud por segundo`,
|
||||
upgrade_WarframeShieldMax: `+|VAL| de escudo`,
|
||||
upgrade_WarframeStartingEnergy: `+|VAL|% de energía al reaparecer`,
|
||||
upgrade_WarframeToxinDamage: `+|VAL|% de daño por efecto de estado tóxico`,
|
||||
|
@ -1,10 +1,11 @@
|
||||
// French translation by Vitruvio
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
|
||||
general_inventoryUpdateNote: `Note : 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_addButton: `Ajouter`,
|
||||
general_setButton: `[UNTRANSLATED] Set`,
|
||||
general_setButton: `Définir`,
|
||||
general_none: `Aucun`,
|
||||
general_bulkActions: `Action groupée`,
|
||||
general_loading: `[UNTRANSLATED] Loading...`,
|
||||
general_loading: `Chargement...`,
|
||||
|
||||
code_loginFail: `Connexion échouée. Vérifiez le mot de passe.`,
|
||||
code_regFail: `Enregistrement impossible. Compte existant?`,
|
||||
@ -45,8 +46,8 @@ dict = {
|
||||
code_focusUnlocked: `|COUNT| écoles de Focus déverrouillées ! Synchronisation de l'inventaire nécessaire.`,
|
||||
code_addModsConfirm: `Ajouter |COUNT| mods à l'inventaire ?`,
|
||||
code_succImport: `Importé.`,
|
||||
code_succRelog: `[UNTRANSLATED] Done. Please note that you'll need to relog to see a difference in-game.`,
|
||||
code_nothingToDo: `[UNTRANSLATED] Done. There was nothing to do.`,
|
||||
code_succRelog: `Succès. Un redémarrage du jeu est nécessaire.`,
|
||||
code_nothingToDo: `Succès.`,
|
||||
code_gild: `Polir`,
|
||||
code_moa: `Moa`,
|
||||
code_zanuka: `Molosse`,
|
||||
@ -61,6 +62,8 @@ dict = {
|
||||
code_pigment: `Pigment`,
|
||||
code_mature: `Maturer pour le combat`,
|
||||
code_unmature: `Régrésser l'âge génétique`,
|
||||
code_succChange: `Changement effectué.`,
|
||||
code_requiredInvigorationUpgrade: `Augmentation offensive et défensive requises.`,
|
||||
login_description: `Connexion avec les informations de connexion OpenWF.`,
|
||||
login_emailLabel: `Email`,
|
||||
login_passwordLabel: `Mot de passe`,
|
||||
@ -122,14 +125,46 @@ dict = {
|
||||
detailedView_archonShardsDescription: `Slots illimités pour appliquer plusieurs améliorations`,
|
||||
detailedView_archonShardsDescription2: `Un délai sera présent entre l'application des éclats et le chargement en jeu.`,
|
||||
detailedView_valenceBonusLabel: `Bonus de Valence`,
|
||||
detailedView_valenceBonusDescription: `[UNTRANSLATED] You can set or remove the Valence Bonus from your weapon.`,
|
||||
detailedView_valenceBonusDescription: `Définir le Bonus Valence de l'arme.`,
|
||||
detailedView_modularPartsLabel: `Changer l'équipement modulaire`,
|
||||
detailedView_suitInvigorationLabel: `Invigoration de Warframe`,
|
||||
detailedView_loadoutLabel: `Équipements`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200% de puissance de pouvoir`,
|
||||
invigorations_offensive_AbilityRange: `+100% de portée de pouvoir`,
|
||||
invigorations_offensive_AbilityDuration: `+100% de durée de pouvoir`,
|
||||
invigorations_offensive_MeleeDamage: `+250% de dégâts de mêlée`,
|
||||
invigorations_offensive_PrimaryDamage: `+250% de dégâts d'arme primaire`,
|
||||
invigorations_offensive_SecondaryDamage: `+250% de dégâts d'arme secondaire`,
|
||||
invigorations_offensive_PrimaryCritChance: `+200% de chances critique sur arme primaire`,
|
||||
invigorations_offensive_SecondaryCritChance: `+200% de chances critique sur arme secondaire`,
|
||||
invigorations_offensive_MeleeCritChance: `+200% de chances critique en mêlée`,
|
||||
|
||||
invigorations_utility_AbilityEfficiency: `+75% d'efficacité de pouvoir`,
|
||||
invigorations_utility_SprintSpeed: `+75% de vitesse de course`,
|
||||
invigorations_utility_ParkourVelocity: `+75% de vélocité de parkour`,
|
||||
invigorations_utility_HealthMax: `+1000 de vie`,
|
||||
invigorations_utility_EnergyMax: `+200% d'énergie max`,
|
||||
invigorations_utility_StatusImmune: `Immunisé contre les effets de statut`,
|
||||
invigorations_utility_ReloadSpeed: `+75% de vitesse de rechargement`,
|
||||
invigorations_utility_HealthRegen: `+25 de vie régénérés/s`,
|
||||
invigorations_utility_ArmorMax: `+1000 d'armure`,
|
||||
invigorations_utility_Jumps: `+5 réinitialisations de saut`,
|
||||
invigorations_utility_EnergyRegen: `+2 d'énergie régénérés/s`,
|
||||
|
||||
invigorations_offensiveLabel: `Amélioration offensive`,
|
||||
invigorations_defensiveLabel: `Amélioration défensive`,
|
||||
invigorations_expiryLabel: `Expiration de l'invigoration (optionnel)`,
|
||||
|
||||
abilityOverride_label: `[UNTRANSLATED] Ability Override`,
|
||||
abilityOverride_onSlot: `[UNTRANSLATED] on slot`,
|
||||
|
||||
mods_addRiven: `Ajouter un riven`,
|
||||
mods_fingerprint: `Empreinte`,
|
||||
mods_fingerprintHelp: `Besoin d'aide pour l'empreinte ?`,
|
||||
mods_rivens: `Rivens`,
|
||||
mods_mods: `Mods`,
|
||||
mods_addMax: `[UNTRANSLATED] Add Maxed`,
|
||||
mods_addMax: `Ajouter les mods niveau max`,
|
||||
mods_addMissingUnrankedMods: `Ajouter les mods sans rang manquants`,
|
||||
mods_removeUnranked: `Retirer les mods sans rang`,
|
||||
mods_addMissingMaxRankMods: `Ajouter les mods niveau max manquants`,
|
||||
@ -139,7 +174,7 @@ dict = {
|
||||
cheats_skipAllDialogue: `Passer les dialogues`,
|
||||
cheats_unlockAllScans: `Débloquer tous les scans`,
|
||||
cheats_unlockAllMissions: `Débloquer toutes les missions`,
|
||||
cheats_unlockAllMissions_ok: `[UNTRANSLATED] Success. Please note that you'll need to enter a dojo/relay or relog for the client to refresh the star chart.`,
|
||||
cheats_unlockAllMissions_ok: `Succès. Une actualisation de l'inventaire est nécessaire.`,
|
||||
cheats_infiniteCredits: `Crédits infinis`,
|
||||
cheats_infinitePlatinum: `Platinum infini`,
|
||||
cheats_infiniteEndo: `Endo infini`,
|
||||
@ -170,8 +205,8 @@ dict = {
|
||||
cheats_noDeathMarks: `Aucune marque d'assassin`,
|
||||
cheats_noKimCooldowns: `Aucun cooldown sur le KIM`,
|
||||
cheats_fullyStockedVendors: `Les vendeurs ont un stock à 100%`,
|
||||
cheats_baroAlwaysAvailable: `[UNTRANSLATED] Baro Always Available`,
|
||||
cheats_baroFullyStocked: `[UNTRANSLATED] Baro Fully Stocked`,
|
||||
cheats_baroAlwaysAvailable: `Baro toujours présent`,
|
||||
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_instantFinishRivenChallenge: `Débloquer le challenge Riven instantanément`,
|
||||
@ -185,76 +220,78 @@ dict = {
|
||||
cheats_noDojoResearchTime: `Aucun temps de recherche (Dojo)`,
|
||||
cheats_fastClanAscension: `Ascension de clan rapide`,
|
||||
cheats_missionsCanGiveAllRelics: `Les missions donnent toutes les reliques`,
|
||||
cheats_exceptionalRelicsAlwaysGiveBronzeReward: `[UNTRANSLATED] Exceptional Relics Always Give Bronze Reward`,
|
||||
cheats_flawlessRelicsAlwaysGiveSilverReward: `[UNTRANSLATED] Flawless Relics Always Give Silver Reward`,
|
||||
cheats_radiantRelicsAlwaysGiveGoldReward: `[UNTRANSLATED] Radiant Relics Always Give Gold Reward`,
|
||||
cheats_exceptionalRelicsAlwaysGiveBronzeReward: `Les reliques exceptionnelles donnent toujours une récompense en bronze`,
|
||||
cheats_flawlessRelicsAlwaysGiveSilverReward: `Les reliques parfaites donnent toujours une récompense en argent`,
|
||||
cheats_radiantRelicsAlwaysGiveGoldReward: `Les reliques éclatantes donnent toujours une récompense en or`,
|
||||
cheats_unlockAllSimarisResearchEntries: `Débloquer toute les recherches chez Simaris`,
|
||||
cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
|
||||
cheats_disableDailyTribute: `Désactiver la récompense quotidienne de connexion`,
|
||||
cheats_spoofMasteryRank: `Rang de maîtrise personnalisé (-1 pour désactiver)`,
|
||||
cheats_relicRewardItemCountMultiplier: `[UNTRANSLATED] Relic Reward Item Count Multiplier`,
|
||||
cheats_relicRewardItemCountMultiplier: `Multiplicateur de récompenses de relique`,
|
||||
cheats_nightwaveStandingMultiplier: `Multiplicateur de réputation d'Ondes Nocturnes`,
|
||||
cheats_save: `Sauvegarder`,
|
||||
cheats_account: `Compte`,
|
||||
cheats_unlockAllFocusSchools: `Débloquer toutes les écoles de focus`,
|
||||
cheats_helminthUnlockAll: `Helminth niveau max`,
|
||||
cheats_addMissingSubsumedAbilities: `[UNTRANSLATED] Add Missing Subsumed Abilities`,
|
||||
cheats_addMissingSubsumedAbilities: `Ajouter les capacités subsumées manquantes`,
|
||||
cheats_intrinsicsUnlockAll: `Inhérences niveau max`,
|
||||
cheats_changeSupportedSyndicate: `Allégeance`,
|
||||
cheats_changeButton: `Changer`,
|
||||
cheats_none: `Aucun`,
|
||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
||||
cheats_markAllAsRead: `Marquer la boîte de réception comme lue`,
|
||||
|
||||
worldState: `[UNTRANSLATED] World State`,
|
||||
worldState_creditBoost: `[UNTRANSLATED] Credit Boost`,
|
||||
worldState_affinityBoost: `[UNTRANSLATED] Affinity Boost`,
|
||||
worldState_resourceBoost: `[UNTRANSLATED] Resource Boost`,
|
||||
worldState_starDays: `[UNTRANSLATED] Star Days`,
|
||||
worldState_galleonOfGhouls: `[UNTRANSLATED] Galleon of Ghouls`,
|
||||
disabled: `[UNTRANSLATED] Disabled`,
|
||||
worldState_we1: `[UNTRANSLATED] Weekend 1`,
|
||||
worldState_we2: `[UNTRANSLATED] Weekend 2`,
|
||||
worldState_we3: `[UNTRANSLATED] Weekend 3`,
|
||||
worldState_eidolonOverride: `[UNTRANSLATED] Eidolon Override`,
|
||||
worldState_day: `[UNTRANSLATED] Day`,
|
||||
worldState_night: `[UNTRANSLATED] Night`,
|
||||
worldState_vallisOverride: `[UNTRANSLATED] Orb Vallis Override`,
|
||||
worldState_warm: `[UNTRANSLATED] Warm`,
|
||||
worldState_cold: `[UNTRANSLATED] Cold`,
|
||||
worldState_duviriOverride: `[UNTRANSLATED] Duviri Override`,
|
||||
worldState_joy: `[UNTRANSLATED] Joy`,
|
||||
worldState_anger: `[UNTRANSLATED] Anger`,
|
||||
worldState_envy: `[UNTRANSLATED] Envy`,
|
||||
worldState_sorrow: `[UNTRANSLATED] Sorrow`,
|
||||
worldState_fear: `[UNTRANSLATED] Fear`,
|
||||
worldState_nightwaveOverride: `[UNTRANSLATED] Nightwave Override`,
|
||||
worldState_RadioLegionIntermission13Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 9`,
|
||||
worldState_RadioLegionIntermission12Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 8`,
|
||||
worldState_RadioLegionIntermission11Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 7`,
|
||||
worldState_RadioLegionIntermission10Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 6`,
|
||||
worldState_RadioLegionIntermission9Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 5`,
|
||||
worldState_RadioLegionIntermission8Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 4`,
|
||||
worldState_RadioLegionIntermission7Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 3`,
|
||||
worldState_RadioLegionIntermission6Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 2`,
|
||||
worldState_RadioLegionIntermission5Syndicate: `[UNTRANSLATED] Nora's Mix Vol. 1`,
|
||||
worldState_RadioLegionIntermission4Syndicate: `[UNTRANSLATED] Nora's Choice`,
|
||||
worldState_RadioLegionIntermission3Syndicate: `[UNTRANSLATED] Intermission III`,
|
||||
worldState_RadioLegion3Syndicate: `[UNTRANSLATED] Glassmaker`,
|
||||
worldState_RadioLegionIntermission2Syndicate: `[UNTRANSLATED] Intermission II`,
|
||||
worldState_RadioLegion2Syndicate: `[UNTRANSLATED] The Emissary`,
|
||||
worldState_RadioLegionIntermissionSyndicate: `[UNTRANSLATED] Intermission I`,
|
||||
worldState_RadioLegionSyndicate: `[UNTRANSLATED] The Wolf of Saturn Six`,
|
||||
worldState_fissures: `[UNTRANSLATED] Fissures`,
|
||||
normal: `[UNTRANSLATED] Normal`,
|
||||
worldState_allAtOnceNormal: `[UNTRANSLATED] All At Once, Normal`,
|
||||
worldState_allAtOnceSteelPath: `[UNTRANSLATED] All At Once, Steel Path`,
|
||||
worldState_theCircuitOverride: `[UNTRANSLATED] The Circuit Override`,
|
||||
worldState_darvoStockMultiplier: `[UNTRANSLATED] Darvo Stock Multiplier`,
|
||||
worldState_varziaFullyStocked: `[UNTRANSLATED] Varzia Fully Stocked`,
|
||||
worldState_varziaOverride: `[UNTRANSLATED] Varzia Rotation Override`,
|
||||
worldState: `Carte Solaire`,
|
||||
worldState_creditBoost: `Booster de Crédit`,
|
||||
worldState_affinityBoost: `Booster d'Affinité`,
|
||||
worldState_resourceBoost: `Booster de Ressource`,
|
||||
worldState_tennoLiveRelay: `[UNTRANSLATED] TennoLive Relay`,
|
||||
worldState_baroTennoConRelay: `[UNTRANSLATED] Baro's TennoCon Relay`,
|
||||
worldState_starDays: `Jours Stellaires`,
|
||||
worldState_galleonOfGhouls: `Galion des Goules`,
|
||||
enabled: `[UNTRANSLATED] Enabled`,
|
||||
disabled: `Désactivé`,
|
||||
worldState_we1: `Weekend 1`,
|
||||
worldState_we2: `Weekend 2`,
|
||||
worldState_we3: `Weekend 3`,
|
||||
worldState_eidolonOverride: `Météo Plaines d'Eidolon`,
|
||||
worldState_day: `Jour`,
|
||||
worldState_night: `Nuit`,
|
||||
worldState_vallisOverride: `Météo Vallée Orbis`,
|
||||
worldState_warm: `Chaud`,
|
||||
worldState_cold: `Froid`,
|
||||
worldState_duviriOverride: `Spirale Duviri`,
|
||||
worldState_joy: `Joie`,
|
||||
worldState_anger: `Colère`,
|
||||
worldState_envy: `Envie `,
|
||||
worldState_sorrow: `hagrin`,
|
||||
worldState_fear: `Peur`,
|
||||
worldState_nightwaveOverride: `Saison d'Ondes Nocturnes`,
|
||||
worldState_RadioLegionIntermission13Syndicate: `Mix de Nora Vol. 9`,
|
||||
worldState_RadioLegionIntermission12Syndicate: `Mix de Nora Vol. 8`,
|
||||
worldState_RadioLegionIntermission11Syndicate: `Mix de Nora Vol. 7`,
|
||||
worldState_RadioLegionIntermission10Syndicate: `Mix de Nora Vol. 6`,
|
||||
worldState_RadioLegionIntermission9Syndicate: `Mix de Nora Vol. 5`,
|
||||
worldState_RadioLegionIntermission8Syndicate: `Mix de Nora Vol. 4`,
|
||||
worldState_RadioLegionIntermission7Syndicate: `Mix de Nora Vol. 3`,
|
||||
worldState_RadioLegionIntermission6Syndicate: `Mix de Nora Vol. 2`,
|
||||
worldState_RadioLegionIntermission5Syndicate: `Mix de Nora Vol. 1`,
|
||||
worldState_RadioLegionIntermission4Syndicate: `La Sélection de Nora`,
|
||||
worldState_RadioLegionIntermission3Syndicate: `Intermission III`,
|
||||
worldState_RadioLegion3Syndicate: `Les Mystères du Verre`,
|
||||
worldState_RadioLegionIntermission2Syndicate: `Intermission II`,
|
||||
worldState_RadioLegion2Syndicate: `L'Émissaire`,
|
||||
worldState_RadioLegionIntermissionSyndicate: `Intermission I`,
|
||||
worldState_RadioLegionSyndicate: `Le Loup de Saturne Six`,
|
||||
worldState_fissures: `Fissures`,
|
||||
normal: `Normal`,
|
||||
worldState_allAtOnceNormal: `Toutes, Normal`,
|
||||
worldState_allAtOnceSteelPath: `Toutes, Route de l'Acier`,
|
||||
worldState_theCircuitOverride: `Remplacement du Circuit`,
|
||||
worldState_darvoStockMultiplier: `Multiplicateur du stock de Darvo`,
|
||||
worldState_varziaFullyStocked: `Stock de Varzia au max`,
|
||||
worldState_varziaOverride: `Rotation de Varzia`,
|
||||
|
||||
import_importNote: `Import manuel. Toutes les modifcations supportées par l'inventaire <b>écraseront celles présentes dans la base de données</b>.`,
|
||||
import_submit: `Soumettre`,
|
||||
import_samples: `Echantillons :`,
|
||||
import_samples: `Échantillons :`,
|
||||
import_samples_maxFocus: `Toutes les écoles de focus au rang max`,
|
||||
|
||||
upgrade_Equilibrium: `Ramasser de la santé donne +|VAL|% d'énergie supplémentaire. Ramasser de l'énergie donne +|VAL|% de santé supplémentaire.`,
|
||||
@ -263,7 +300,7 @@ dict = {
|
||||
upgrade_SecondaryCritChance: `+|VAL|% de chance critique sur arme secondaire`,
|
||||
upgrade_WarframeAbilityDuration: `+|VAL|% de durée de pouvoir`,
|
||||
upgrade_WarframeAbilityStrength: `+|VAL|% de puissance de pouvoir`,
|
||||
upgrade_WarframeArmourMax: `+|VAL| d'armure`,
|
||||
upgrade_WarframeArmorMax: `+|VAL| d'armure`,
|
||||
upgrade_WarframeBlastProc: `+|VAL| de boucliers sur élimination avec des dégats d'explosion`,
|
||||
upgrade_WarframeCastingSpeed: `+|VAL|% de vitesse de lancement de pouvoir`,
|
||||
upgrade_WarframeCorrosiveDamageBoost: `+|VAL|% de dégâts de pouvoir sur les ennemis affectés par du statut corrosif`,
|
||||
@ -275,10 +312,10 @@ dict = {
|
||||
upgrade_WarframeGlobeEffectEnergy: `+|VAL|% d'efficacité d'orbe d'énergie`,
|
||||
upgrade_WarframeGlobeEffectHealth: `+|VAL|% d'efficacité d'orbe de santé`,
|
||||
upgrade_WarframeHealthMax: `+|VAL| de santé`,
|
||||
upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
|
||||
upgrade_WarframeHPBoostFromImpact: `+|VAL1| de vie sur élimination avec des dégâts d'explostion (Max |VAL2| Health)`,
|
||||
upgrade_WarframeParkourVelocity: `+|VAL|% de vélocité de parkour`,
|
||||
upgrade_WarframeRadiationDamageBoost: `+|VAL|% de dégâts de pouvoir sur les ennemis affectés par du statut radiation`,
|
||||
upgrade_WarframeRegen: `+|VAL| régénération de santé/s`,
|
||||
upgrade_WarframeHealthRegen: `+|VAL| régénération de santé/s`,
|
||||
upgrade_WarframeShieldMax: `+|VAL| de boucliers`,
|
||||
upgrade_WarframeStartingEnergy: `+|VAL|% d'énergie sur apparition`,
|
||||
upgrade_WarframeToxinDamage: `+|VAL|% de dégâts sur le statut poison`,
|
||||
@ -294,7 +331,7 @@ dict = {
|
||||
upgrade_OnExecutionAmmo: `100% de rechargement des armes primaires et secondaires sur une une miséricorde`,
|
||||
upgrade_OnExecutionHealthDrop: `100% de chance de drop une orbe de santé sur une miséricorde`,
|
||||
upgrade_OnExecutionEnergyDrop: `50% de chance de drop une orbe d'énergie sur une miséricorde`,
|
||||
upgrade_OnFailHackReset: `[UNTRANSLATED] +50% Hacking Retry Chance`,
|
||||
upgrade_OnFailHackReset: `+50% de chance de refaire un piratage`,
|
||||
upgrade_DamageReductionOnHack: `75% de réduction de dégâts pendant un piratage`,
|
||||
upgrade_OnExecutionReviveCompanion: `Les miséricordes réduisent le temps de récupération du compagnon de 15s`,
|
||||
upgrade_OnExecutionParkourSpeed: `+60% de vitesse de parkour pendant 15s après une miséricorde`,
|
||||
@ -303,10 +340,10 @@ dict = {
|
||||
upgrade_OnExecutionTerrify: `Les ennemis dans un rayon de 15m ont 50% de chance de s'enfuir après une miséricorde`,
|
||||
upgrade_OnHackLockers: `5 casiers s'ouvrent dans un rayon de 20m après un piratage`,
|
||||
upgrade_OnExecutionBlind: `Les ennemis sont aveuglés dans un rayon de 18 après une miséricorde`,
|
||||
upgrade_OnExecutionDrainPower: `[UNTRANSLATED] Next ability cast gains +50% Ability Strength on Mercy`,
|
||||
upgrade_OnExecutionDrainPower: `Le prochain pouvoir activé gagne +50% de puissance de pouvoir après une miséricorde`,
|
||||
upgrade_OnHackSprintSpeed: `+75% de vitesse de course pendant 15s après un piratage`,
|
||||
upgrade_SwiftExecute: `[UNTRANSLATED] +50% Mercy Kill Speed`,
|
||||
upgrade_OnHackInvis: `[UNTRANSLATED] Invisible for 15 seconds after Hacking`,
|
||||
upgrade_SwiftExecute: `+50% de vitesse de d'éxecution en miséricorde`,
|
||||
upgrade_OnHackInvis: `Invisible pendant 15s après un piratage`,
|
||||
|
||||
damageType_Electricity: `Électrique`,
|
||||
damageType_Fire: `Feu`,
|
||||
@ -316,8 +353,8 @@ dict = {
|
||||
damageType_Poison: `Poison`,
|
||||
damageType_Radiation: `Radiations`,
|
||||
|
||||
theme_dark: `[UNTRANSLATED] Dark Theme`,
|
||||
theme_light: `[UNTRANSLATED] Light Theme`,
|
||||
theme_dark: `Thème sombre`,
|
||||
theme_light: `Thème clair`,
|
||||
|
||||
prettier_sucks_ass: ``
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ dict = {
|
||||
general_inventoryUpdateNote: `[UNTRANSLATED] Note: To see changes in-game, you need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging.`,
|
||||
general_addButton: `Добавить`,
|
||||
general_setButton: `Установить`,
|
||||
general_none: `Отсутствует`,
|
||||
general_bulkActions: `Массовые действия`,
|
||||
general_loading: `[UNTRANSLATED] Loading...`,
|
||||
|
||||
@ -61,6 +62,8 @@ dict = {
|
||||
code_pigment: `Пигмент`,
|
||||
code_mature: `Подготовить к сражениям`,
|
||||
code_unmature: `Регрессия генетического старения`,
|
||||
code_succChange: `Успешно изменено.`,
|
||||
code_requiredInvigorationUpgrade: `[UNTRANSLATED] You must select both an offensive & defensive upgrade.`,
|
||||
login_description: `Войдите, используя учетные данные OpenWF (те же, что и в игре при подключении к этому серверу).`,
|
||||
login_emailLabel: `Адрес электронной почты`,
|
||||
login_passwordLabel: `Пароль`,
|
||||
@ -123,6 +126,38 @@ dict = {
|
||||
detailedView_archonShardsDescription2: `Обратите внимание: каждый фрагмент архонта применяется с задержкой при загрузке.`,
|
||||
detailedView_valenceBonusLabel: `Бонус Валентности`,
|
||||
detailedView_valenceBonusDescription: `Вы можете установить или убрать бонус валентности с вашего оружия.`,
|
||||
detailedView_modularPartsLabel: `Изменить Модульные Части`,
|
||||
detailedView_suitInvigorationLabel: `[UNTRANSLATED] Warframe Invigoration`,
|
||||
detailedView_loadoutLabel: `Конфигурации`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `[UNTRANSLATED] +200% Ability Strength`,
|
||||
invigorations_offensive_AbilityRange: `[UNTRANSLATED] +100% Ability Range`,
|
||||
invigorations_offensive_AbilityDuration: `[UNTRANSLATED] +100% Ability Duration`,
|
||||
invigorations_offensive_MeleeDamage: `[UNTRANSLATED] +250% Melee Damage`,
|
||||
invigorations_offensive_PrimaryDamage: `[UNTRANSLATED] +250% Primary Damage`,
|
||||
invigorations_offensive_SecondaryDamage: `[UNTRANSLATED] +250% Secondary Damage`,
|
||||
invigorations_offensive_PrimaryCritChance: `[UNTRANSLATED] +200% Primary Critical Chance`,
|
||||
invigorations_offensive_SecondaryCritChance: `[UNTRANSLATED] +200% Secondary Critical Chance`,
|
||||
invigorations_offensive_MeleeCritChance: `[UNTRANSLATED] +200% Melee Critical Chance`,
|
||||
|
||||
invigorations_utility_AbilityEfficiency: `[UNTRANSLATED] +75% Ability Efficiency`,
|
||||
invigorations_utility_SprintSpeed: `[UNTRANSLATED] +75% Sprint Speed`,
|
||||
invigorations_utility_ParkourVelocity: `[UNTRANSLATED] +75% Parkour Velocity`,
|
||||
invigorations_utility_HealthMax: `[UNTRANSLATED] +1000 Health`,
|
||||
invigorations_utility_EnergyMax: `[UNTRANSLATED] +200% Energy Max`,
|
||||
invigorations_utility_StatusImmune: `[UNTRANSLATED] Immune to Status Effects`,
|
||||
invigorations_utility_ReloadSpeed: `[UNTRANSLATED] +75% Reload Speed`,
|
||||
invigorations_utility_HealthRegen: `[UNTRANSLATED] +25 Health Regen/s`,
|
||||
invigorations_utility_ArmorMax: `[UNTRANSLATED] +1000 Armor`,
|
||||
invigorations_utility_Jumps: `[UNTRANSLATED] +5 Jump Resets`,
|
||||
invigorations_utility_EnergyRegen: `[UNTRANSLATED] +2 Energy Regen/s`,
|
||||
|
||||
invigorations_offensiveLabel: `[UNTRANSLATED] Offensive Upgrade`,
|
||||
invigorations_defensiveLabel: `[UNTRANSLATED] Defensive Upgrade`,
|
||||
invigorations_expiryLabel: `[UNTRANSLATED] Upgrades Expiry (optional)`,
|
||||
|
||||
abilityOverride_label: `Переопределение способности`,
|
||||
abilityOverride_onSlot: `в ячейке`,
|
||||
|
||||
mods_addRiven: `Добавить Мод Разлома`,
|
||||
mods_fingerprint: `Отпечаток`,
|
||||
@ -201,22 +236,24 @@ dict = {
|
||||
cheats_intrinsicsUnlockAll: `Полностью улучшить Модуляры`,
|
||||
cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
|
||||
cheats_changeButton: `Изменить`,
|
||||
cheats_none: `Отсутствует`,
|
||||
cheats_markAllAsRead: `[UNTRANSLATED] Mark Inbox As Read`,
|
||||
|
||||
worldState: `[UNTRANSLATED] World State`,
|
||||
worldState_creditBoost: `[UNTRANSLATED] Credit Boost`,
|
||||
worldState_affinityBoost: `[UNTRANSLATED] Affinity Boost`,
|
||||
worldState_resourceBoost: `[UNTRANSLATED] Resource Boost`,
|
||||
worldState_tennoLiveRelay: `[UNTRANSLATED] TennoLive Relay`,
|
||||
worldState_baroTennoConRelay: `[UNTRANSLATED] Baro's TennoCon Relay`,
|
||||
worldState_starDays: `[UNTRANSLATED] Star Days`,
|
||||
worldState_galleonOfGhouls: `[UNTRANSLATED] Galleon of Ghouls`,
|
||||
enabled: `[UNTRANSLATED] Enabled`,
|
||||
disabled: `[UNTRANSLATED] Disabled`,
|
||||
worldState_we1: `[UNTRANSLATED] Weekend 1`,
|
||||
worldState_we2: `[UNTRANSLATED] Weekend 2`,
|
||||
worldState_we3: `[UNTRANSLATED] Weekend 3`,
|
||||
worldState_eidolonOverride: `[UNTRANSLATED] Eidolon Override`,
|
||||
worldState_day: `[UNTRANSLATED] Day`,
|
||||
worldState_night: `[UNTRANSLATED] Night`,
|
||||
worldState_eidolonOverride: `[UNTRANSLATED] Eidolon/Deimos Override`,
|
||||
worldState_day: `[UNTRANSLATED] Day/Fass`,
|
||||
worldState_night: `[UNTRANSLATED] Night/Vome`,
|
||||
worldState_vallisOverride: `[UNTRANSLATED] Orb Vallis Override`,
|
||||
worldState_warm: `[UNTRANSLATED] Warm`,
|
||||
worldState_cold: `[UNTRANSLATED] Cold`,
|
||||
@ -263,7 +300,7 @@ dict = {
|
||||
upgrade_SecondaryCritChance: `[UNTRANSLATED] +|VAL|% Secondary Critical Chance`,
|
||||
upgrade_WarframeAbilityDuration: `[UNTRANSLATED] +|VAL|% Ability Duration`,
|
||||
upgrade_WarframeAbilityStrength: `[UNTRANSLATED] +|VAL|% Ability Strength`,
|
||||
upgrade_WarframeArmourMax: `[UNTRANSLATED] +|VAL| Armor`,
|
||||
upgrade_WarframeArmorMax: `[UNTRANSLATED] +|VAL| Armor`,
|
||||
upgrade_WarframeBlastProc: `[UNTRANSLATED] +|VAL| Shields on kill with Blast Damage`,
|
||||
upgrade_WarframeCastingSpeed: `[UNTRANSLATED] +|VAL|% Casting Speed`,
|
||||
upgrade_WarframeCorrosiveDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Corrosion Status`,
|
||||
@ -278,7 +315,7 @@ dict = {
|
||||
upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health on kill with Blast Damage (Max |VAL2| Health)`,
|
||||
upgrade_WarframeParkourVelocity: `[UNTRANSLATED] +|VAL|% Parkour Velocity`,
|
||||
upgrade_WarframeRadiationDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Radiation Status`,
|
||||
upgrade_WarframeRegen: `[UNTRANSLATED] +|VAL| Health Regen/s`,
|
||||
upgrade_WarframeHealthRegen: `[UNTRANSLATED] +|VAL| Health Regen/s`,
|
||||
upgrade_WarframeShieldMax: `[UNTRANSLATED] +|VAL| Shield`,
|
||||
upgrade_WarframeStartingEnergy: `[UNTRANSLATED] +|VAL|% Energy on Spawn`,
|
||||
upgrade_WarframeToxinDamage: `[UNTRANSLATED] +|VAL|% Toxin Status Effect Damage`,
|
||||
|
@ -1,8 +1,9 @@
|
||||
// Chinese translation by meb154, bishan178 & Corvus
|
||||
// Chinese translation by meb154, bishan178, nyaoouo, qianlishun, CrazyZhang & Corvus
|
||||
dict = {
|
||||
general_inventoryUpdateNote: `注意:要在游戏中查看更改,您需要重新同步库存,例如使用引导程序的 /sync 命令、访问道场/中继站或重新登录客户端.`,
|
||||
general_addButton: `添加`,
|
||||
general_setButton: `设置`,
|
||||
general_none: `无`,
|
||||
general_bulkActions: `批量操作`,
|
||||
general_loading: `加载中...`,
|
||||
|
||||
@ -61,6 +62,8 @@ dict = {
|
||||
code_pigment: `颜料`,
|
||||
code_mature: `成长并战备`,
|
||||
code_unmature: `逆转衰老基因`,
|
||||
code_succChange: `更改成功.`,
|
||||
code_requiredInvigorationUpgrade: `您必须同时选择一个进攻型和一个功能型活化属性.`,
|
||||
login_description: `使用您的 OpenWF 账户凭证登录(与游戏内连接本服务器时使用的昵称相同).`,
|
||||
login_emailLabel: `电子邮箱`,
|
||||
login_passwordLabel: `密码`,
|
||||
@ -123,6 +126,38 @@ dict = {
|
||||
detailedView_archonShardsDescription2: `请注意,在加载时,每个执政官源力石都需要一定的时间来生效。`,
|
||||
detailedView_valenceBonusLabel: `效价加成`,
|
||||
detailedView_valenceBonusDescription: `您可以设置或移除武器上的效价加成.`,
|
||||
detailedView_modularPartsLabel: `更换部件`,
|
||||
detailedView_suitInvigorationLabel: `编辑战甲活化属性`,
|
||||
detailedView_loadoutLabel: `配置`,
|
||||
|
||||
invigorations_offensive_AbilityStrength: `+200%技能强度`,
|
||||
invigorations_offensive_AbilityRange: `+100%技能范围`,
|
||||
invigorations_offensive_AbilityDuration: `+100%技能持续时间`,
|
||||
invigorations_offensive_MeleeDamage: `+250%近战武器伤害`,
|
||||
invigorations_offensive_PrimaryDamage: `+250%主要武器伤害`,
|
||||
invigorations_offensive_SecondaryDamage: `+250%次要武器伤害`,
|
||||
invigorations_offensive_PrimaryCritChance: `+200%主要武器暴击几率`,
|
||||
invigorations_offensive_SecondaryCritChance: `+200%次要武器暴击几率`,
|
||||
invigorations_offensive_MeleeCritChance: `+200%近战武器暴击几率`,
|
||||
|
||||
invigorations_utility_AbilityEfficiency: `+75%技能效率`,
|
||||
invigorations_utility_SprintSpeed: `+75%冲刺速度`,
|
||||
invigorations_utility_ParkourVelocity: `+75%跑酷速度`,
|
||||
invigorations_utility_HealthMax: `+1000生命值`,
|
||||
invigorations_utility_EnergyMax: `+200%最大能量`,
|
||||
invigorations_utility_StatusImmune: `异常状态免疫`,
|
||||
invigorations_utility_ReloadSpeed: `+75%装填速度`,
|
||||
invigorations_utility_HealthRegen: `+25/秒生命再生`,
|
||||
invigorations_utility_ArmorMax: `+1000护甲值`,
|
||||
invigorations_utility_Jumps: `+5跳跃次数`,
|
||||
invigorations_utility_EnergyRegen: `+2/秒能量再生`,
|
||||
|
||||
invigorations_offensiveLabel: `进攻型属性`,
|
||||
invigorations_defensiveLabel: `功能型属性`,
|
||||
invigorations_expiryLabel: `活化时效(可选)`,
|
||||
|
||||
abilityOverride_label: `技能替换`,
|
||||
abilityOverride_onSlot: `槽位`,
|
||||
|
||||
mods_addRiven: `添加裂罅MOD`,
|
||||
mods_fingerprint: `印记`,
|
||||
@ -201,15 +236,17 @@ dict = {
|
||||
cheats_intrinsicsUnlockAll: `所有内源之力最大等级`,
|
||||
cheats_changeSupportedSyndicate: `支持的集团`,
|
||||
cheats_changeButton: `更改`,
|
||||
cheats_none: `无`,
|
||||
cheats_markAllAsRead: `收件箱全部标记为已读`,
|
||||
|
||||
worldState: `世界状态配置`,
|
||||
worldState_creditBoost: `现金加成`,
|
||||
worldState_affinityBoost: `经验加成`,
|
||||
worldState_resourceBoost: `资源加成`,
|
||||
worldState_tennoLiveRelay: `TennoLive中继站`,
|
||||
worldState_baroTennoConRelay: `Baro的TennoCon中继站`,
|
||||
worldState_starDays: `活动:星日`,
|
||||
worldState_galleonOfGhouls: `战术警报:尸鬼的帆船战舰`,
|
||||
enabled: `启用`,
|
||||
disabled: `关闭/取消配置`,
|
||||
worldState_we1: `活动阶段:第一周`,
|
||||
worldState_we2: `活动阶段:第二周`,
|
||||
@ -263,7 +300,7 @@ dict = {
|
||||
upgrade_SecondaryCritChance: `+|VAL|%次要武器暴击几率`,
|
||||
upgrade_WarframeAbilityDuration: `+|VAL|%技能持续时间`,
|
||||
upgrade_WarframeAbilityStrength: `+|VAL|%技能强度`,
|
||||
upgrade_WarframeArmourMax: `+|VAL|护甲`,
|
||||
upgrade_WarframeArmorMax: `+|VAL|护甲`,
|
||||
upgrade_WarframeBlastProc: `使用爆炸伤害击杀敌人时恢复+|VAL|护盾`,
|
||||
upgrade_WarframeCastingSpeed: `+|VAL|%施放速度`,
|
||||
upgrade_WarframeCorrosiveDamageBoost: `对受腐蚀异常影响的敌人+|VAL|%额外技能伤害`,
|
||||
@ -278,7 +315,7 @@ dict = {
|
||||
upgrade_WarframeHPBoostFromImpact: `通过爆炸伤害击杀的每个敌人可增加|VAL1|生命上限(最大|VAL2|生命上限)`,
|
||||
upgrade_WarframeParkourVelocity: `+|VAL|%跑酷速度`,
|
||||
upgrade_WarframeRadiationDamageBoost: `对受辐射状态影响的敌人+|VAL|%技能伤害`,
|
||||
upgrade_WarframeRegen: `+|VAL|/秒生命再生`,
|
||||
upgrade_WarframeHealthRegen: `+|VAL|/秒生命再生`,
|
||||
upgrade_WarframeShieldMax: `+|VAL|护盾值`,
|
||||
upgrade_WarframeStartingEnergy: `进任务时+|VAL|%初始能量`,
|
||||
upgrade_WarframeToxinDamage: `毒素伤害额外+|VAL|%伤害`,
|
||||
|
Loading…
x
Reference in New Issue
Block a user