Compare commits
	
		
			4 Commits
		
	
	
		
			6df625e838
			...
			8d54b2c043
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8d54b2c043 | |||
| b23979855e | |||
| c835471f0f | |||
| f82bd09ee4 | 
							
								
								
									
										17
									
								
								AGENTS.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								AGENTS.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					## In General
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Prerequisites
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Use `npm i` or `npm ci` to install all dependencies.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Testing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Use `npm run verify` to verify that your changes pass TypeScript's checks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Formatting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Use `npm run prettier` to ensure your formatting matches the expected format. Failing to do so will cause CI failure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## WebUI Specific
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The translation system is designed around additions being made to `static/webui/translations/en.js`. They are copied over for translation via `npm run update-translations`. DO NOT produce non-English strings; we want them to be translated by humans who can understand the full context.
 | 
				
			||||||
@ -1,19 +0,0 @@
 | 
				
			|||||||
## In General
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Prerequisites
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Use `npm i` or `npm ci` to install all dependencies, including dev dependencies.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Development Process
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Auto reloading is supported for server and WebUI development. Simply use `npm run dev` or `npm run dev:bun` to start the server and edit away.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Testing
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Before submitting a PR:
 | 
					 | 
				
			||||||
- Use `npm run verify` to verify that the code is type-safe.
 | 
					 | 
				
			||||||
- Use `npm run fix` to fix formatting issues as well as be informed of any unfixable issues. Avoid introducing new warnings.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## WebUI Specific
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The translation system is designed around additions being made to `static/webui/translations/en.js`. They are copied over for translation via `npm run update-translations`. DO NOT provide translations generated by AI or other automated tools.
 | 
					 | 
				
			||||||
@ -6,15 +6,14 @@ More information for the moment here: [https://discord.gg/PNNZ3asUuY](https://di
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
This project is in active development at <https://onlyg.it/OpenWF/SpaceNinjaServer>.
 | 
					This project is in active development at <https://onlyg.it/OpenWF/SpaceNinjaServer>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To get an idea of what functionality you can expect to be missing [have a look through the issues](https://onlyg.it/OpenWF/SpaceNinjaServer/issues). However, many things have been implemented and *should* work as expected. Please open an issue for anything where that's not the case and/or the server is reporting errors.
 | 
					To get an idea of what functionality you can expect to be missing [have a look through the issues](https://onlyg.it/OpenWF/SpaceNinjaServer/issues?q=&type=all&state=open&labels=-4%2C-10&milestone=0&assignee=0&poster=). However, many things have been implemented and *should* work as expected. Please open an issue for anything where that's not the case and/or the server is reporting errors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## config.json
 | 
					## config.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [config-vanilla.json](config-vanilla.json), 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `skipTutorial` affects only newly created accounts, so you may wish to change it before logging in for the first time.
 | 
					 | 
				
			||||||
- `logger.level` can be `fatal`, `error`, `warn`, `info`, `http`, `debug`, or `trace`.
 | 
					- `logger.level` can be `fatal`, `error`, `warn`, `info`, `http`, `debug`, or `trace`.
 | 
				
			||||||
- `ircAddress`, `hubAddress`, and `nrsAddress` are not present by default but can be provided if these secondary servers are on a different machine.
 | 
					- `myIrcAddresses` can be used to point to an IRC server. If not provided, defaults to `[ myAddress ]`.
 | 
				
			||||||
- `worldState.eidolonOverride` can be set to `day` or `night` to lock the time to day/fass and night/vome on Plains of Eidolon/Cambion Drift.
 | 
					- `worldState.eidolonOverride` can be set to `day` or `night` to lock the time to day/fass and night/vome on Plains of Eidolon/Cambion Drift.
 | 
				
			||||||
- `worldState.vallisOverride` can be set to `warm` or `cold` to lock the temperature on Orb Vallis.
 | 
					- `worldState.vallisOverride` can be set to `warm` or `cold` to lock the temperature on Orb Vallis.
 | 
				
			||||||
- `worldState.duviriOverride` can be set to `joy`, `anger`, `envy`, `sorrow`, or `fear` to lock the Duviri spiral.
 | 
					- `worldState.duviriOverride` can be set to `joy`, `anger`, `envy`, `sorrow`, or `fear` to lock the Duviri spiral.
 | 
				
			||||||
 | 
				
			|||||||
@ -14,18 +14,13 @@ if %errorlevel% == 0 (
 | 
				
			|||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	echo Updating dependencies...
 | 
						echo Updating dependencies...
 | 
				
			||||||
	node scripts/raw-precheck.js > NUL
 | 
					 | 
				
			||||||
	if %errorlevel% == 0 (
 | 
					 | 
				
			||||||
		call npm i --omit=dev --omit=optional
 | 
					 | 
				
			||||||
		call npm run raw
 | 
					 | 
				
			||||||
	) else (
 | 
					 | 
				
			||||||
	call npm i --omit=dev
 | 
						call npm i --omit=dev
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	call npm run build
 | 
						call npm run build
 | 
				
			||||||
	if %errorlevel% == 0 (
 | 
						if %errorlevel% == 0 (
 | 
				
			||||||
		call npm run start
 | 
							call npm run start
 | 
				
			||||||
		)
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
		echo SpaceNinjaServer seems to have crashed.
 | 
							echo SpaceNinjaServer seems to have crashed.
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:a
 | 
					:a
 | 
				
			||||||
 | 
				
			|||||||
@ -14,16 +14,11 @@ if [ $? -eq 0 ]; then
 | 
				
			|||||||
    fi
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    echo "Updating dependencies..."
 | 
					    echo "Updating dependencies..."
 | 
				
			||||||
    node scripts/raw-precheck.js > /dev/null
 | 
					 | 
				
			||||||
    if [ $? -eq 0 ]; then
 | 
					 | 
				
			||||||
        npm i --omit=dev --omit=optional
 | 
					 | 
				
			||||||
        npm run raw
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
    npm i --omit=dev
 | 
					    npm i --omit=dev
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    npm run build
 | 
					    npm run build
 | 
				
			||||||
    if [ $? -eq 0 ]; then
 | 
					    if [ $? -eq 0 ]; then
 | 
				
			||||||
        npm run start
 | 
					        npm run start
 | 
				
			||||||
        fi
 | 
					 | 
				
			||||||
    fi
 | 
					 | 
				
			||||||
        echo "SpaceNinjaServer seems to have crashed."
 | 
					        echo "SpaceNinjaServer seems to have crashed."
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
				
			|||||||
@ -10,9 +10,12 @@
 | 
				
			|||||||
  "administratorNames": [],
 | 
					  "administratorNames": [],
 | 
				
			||||||
  "autoCreateAccount": true,
 | 
					  "autoCreateAccount": true,
 | 
				
			||||||
  "skipTutorial": false,
 | 
					  "skipTutorial": false,
 | 
				
			||||||
 | 
					  "unlockAllScans": false,
 | 
				
			||||||
 | 
					  "unlockAllShipFeatures": false,
 | 
				
			||||||
  "unlockAllShipDecorations": false,
 | 
					  "unlockAllShipDecorations": false,
 | 
				
			||||||
  "unlockAllFlavourItems": false,
 | 
					  "unlockAllFlavourItems": false,
 | 
				
			||||||
  "unlockAllSkins": false,
 | 
					  "unlockAllSkins": false,
 | 
				
			||||||
 | 
					  "unlockAllCapturaScenes": false,
 | 
				
			||||||
  "fullyStockedVendors": false,
 | 
					  "fullyStockedVendors": false,
 | 
				
			||||||
  "skipClanKeyCrafting": false,
 | 
					  "skipClanKeyCrafting": false,
 | 
				
			||||||
  "noDojoRoomBuildStage": false,
 | 
					  "noDojoRoomBuildStage": false,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -33,9 +33,6 @@
 | 
				
			|||||||
        "prettier": "^3.5.3",
 | 
					        "prettier": "^3.5.3",
 | 
				
			||||||
        "tree-kill": "^1.2.2"
 | 
					        "tree-kill": "^1.2.2"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=20.18.1"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "optionalDependencies": {
 | 
					      "optionalDependencies": {
 | 
				
			||||||
        "@types/express": "^5",
 | 
					        "@types/express": "^5",
 | 
				
			||||||
        "@types/morgan": "^1.9.9",
 | 
					        "@types/morgan": "^1.9.9",
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@
 | 
				
			|||||||
    "dev:bun": "bun scripts/dev.cjs",
 | 
					    "dev:bun": "bun scripts/dev.cjs",
 | 
				
			||||||
    "verify": "tsgo --noEmit",
 | 
					    "verify": "tsgo --noEmit",
 | 
				
			||||||
    "verify:tsc": "tsc --noEmit",
 | 
					    "verify:tsc": "tsc --noEmit",
 | 
				
			||||||
    "raw": "node scripts/raw-precheck.js && node --experimental-transform-types src/index.ts",
 | 
					    "raw": "node --experimental-transform-types src/index.ts",
 | 
				
			||||||
    "raw:bun": "bun src/index.ts",
 | 
					    "raw:bun": "bun src/index.ts",
 | 
				
			||||||
    "lint": "eslint --ext .ts .",
 | 
					    "lint": "eslint --ext .ts .",
 | 
				
			||||||
    "lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
 | 
					    "lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
 | 
				
			||||||
 | 
				
			|||||||
@ -13,17 +13,6 @@ args.push("--dev");
 | 
				
			|||||||
args.push("--secret");
 | 
					args.push("--secret");
 | 
				
			||||||
args.push(secret);
 | 
					args.push(secret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cangoraw = (() => {
 | 
					 | 
				
			||||||
    if (process.versions.bun) {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const [major, minor] = process.versions.node.split(".").map(x => parseInt(x));
 | 
					 | 
				
			||||||
    if (major > 22 || (major == 22 && minor >= 7)) {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
})();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let buildproc, runproc;
 | 
					let buildproc, runproc;
 | 
				
			||||||
const spawnopts = { stdio: "inherit", shell: true };
 | 
					const spawnopts = { stdio: "inherit", shell: true };
 | 
				
			||||||
function run(changedFile) {
 | 
					function run(changedFile) {
 | 
				
			||||||
@ -40,7 +29,7 @@ function run(changedFile) {
 | 
				
			|||||||
        runproc = undefined;
 | 
					        runproc = undefined;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const thisbuildproc = spawn("npm", ["run", cangoraw ? "verify" : "build:dev"], spawnopts);
 | 
					    const thisbuildproc = spawn("npm", ["run", process.versions.bun ? "verify" : "build:dev"], spawnopts);
 | 
				
			||||||
    const thisbuildstart = Date.now();
 | 
					    const thisbuildstart = Date.now();
 | 
				
			||||||
    buildproc = thisbuildproc;
 | 
					    buildproc = thisbuildproc;
 | 
				
			||||||
    buildproc.on("exit", code => {
 | 
					    buildproc.on("exit", code => {
 | 
				
			||||||
@ -49,12 +38,8 @@ function run(changedFile) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        buildproc = undefined;
 | 
					        buildproc = undefined;
 | 
				
			||||||
        if (code === 0) {
 | 
					        if (code === 0) {
 | 
				
			||||||
            console.log(`${cangoraw ? "Verified" : "Built"} in ${Date.now() - thisbuildstart} ms`);
 | 
					            console.log(`${process.versions.bun ? "Verified" : "Built"} in ${Date.now() - thisbuildstart} ms`);
 | 
				
			||||||
            runproc = spawn(
 | 
					            runproc = spawn("npm", ["run", process.versions.bun ? "raw:bun" : "start", "--", ...args], spawnopts);
 | 
				
			||||||
                "npm",
 | 
					 | 
				
			||||||
                ["run", cangoraw ? (process.versions.bun ? "raw:bun" : "raw") : "start", "--", ...args],
 | 
					 | 
				
			||||||
                spawnopts
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            runproc.on("exit", () => {
 | 
					            runproc.on("exit", () => {
 | 
				
			||||||
                runproc = undefined;
 | 
					                runproc = undefined;
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
const [major, minor] = process.versions.node.split(".").map(x => parseInt(x));
 | 
					 | 
				
			||||||
if (major > 22 || (major == 22 && minor >= 7)) {
 | 
					 | 
				
			||||||
    // ok
 | 
					 | 
				
			||||||
} else {
 | 
					 | 
				
			||||||
    console.log("Sorry, your Node version is a bit too old for this. You have 2 options:");
 | 
					 | 
				
			||||||
    console.log("- Update Node.js.");
 | 
					 | 
				
			||||||
    console.log("- Use 'npm run build && npm run start'. Optional libraries must be installed for this.");
 | 
					 | 
				
			||||||
    process.exit(1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,4 +1,6 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					import type { RequestHandler } from "express";
 | 
				
			||||||
 | 
					import { config } from "../../services/configService.ts";
 | 
				
			||||||
 | 
					import allShipFeatures from "../../../static/fixed_responses/allShipFeatures.json" with { type: "json" };
 | 
				
			||||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
					import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
				
			||||||
import { createGarden, getPersonalRooms } from "../../services/personalRoomsService.ts";
 | 
					import { createGarden, getPersonalRooms } from "../../services/personalRoomsService.ts";
 | 
				
			||||||
import type { IGetShipResponse, IPersonalRoomsClient } from "../../types/personalRoomsTypes.ts";
 | 
					import type { IGetShipResponse, IPersonalRoomsClient } from "../../types/personalRoomsTypes.ts";
 | 
				
			||||||
@ -29,5 +31,9 @@ export const getShipController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        TailorShop: personalRooms.TailorShop
 | 
					        TailorShop: personalRooms.TailorShop
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (config.unlockAllShipFeatures) {
 | 
				
			||||||
 | 
					        getShipResponse.Ship.Features = allShipFeatures;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.json(getShipResponse);
 | 
					    res.json(getShipResponse);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					import type { RequestHandler } from "express";
 | 
				
			||||||
import { config, getReflexiveAddress } from "../../services/configService.ts";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const hubController: RequestHandler = (req, res) => {
 | 
					const hubController: RequestHandler = (_req, res) => {
 | 
				
			||||||
    res.json(`hub ${config.hubAddress ?? getReflexiveAddress(req).myAddress}:6952`);
 | 
					    res.json("hub 127.0.0.1:6952");
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { hubController };
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ import { equipmentKeys } from "../../types/inventoryTypes/inventoryTypes.ts";
 | 
				
			|||||||
import type { IPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
 | 
					import type { IPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
 | 
				
			||||||
import { ArtifactPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
 | 
					import { ArtifactPolarity } from "../../types/inventoryTypes/commonInventoryTypes.ts";
 | 
				
			||||||
import type { ICountedItem } from "warframe-public-export-plus";
 | 
					import type { ICountedItem } from "warframe-public-export-plus";
 | 
				
			||||||
import { eFaction, ExportCustoms, ExportFlavour, ExportResources } from "warframe-public-export-plus";
 | 
					import { eFaction, ExportCustoms, ExportFlavour, ExportResources, ExportVirtuals } from "warframe-public-export-plus";
 | 
				
			||||||
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "../../services/infestedFoundryService.ts";
 | 
					import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "../../services/infestedFoundryService.ts";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    addEmailItem,
 | 
					    addEmailItem,
 | 
				
			||||||
@ -358,6 +358,17 @@ export const getInventoryResponse = async (
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (config.unlockAllCapturaScenes) {
 | 
				
			||||||
 | 
					        for (const uniqueName of Object.keys(ExportResources)) {
 | 
				
			||||||
 | 
					            if (resourceInheritsFrom(uniqueName, "/Lotus/Types/Items/MiscItems/PhotoboothTile")) {
 | 
				
			||||||
 | 
					                inventoryResponse.MiscItems.push({
 | 
				
			||||||
 | 
					                    ItemType: uniqueName,
 | 
				
			||||||
 | 
					                    ItemCount: 1
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (typeof config.spoofMasteryRank === "number" && config.spoofMasteryRank >= 0) {
 | 
					    if (typeof config.spoofMasteryRank === "number" && config.spoofMasteryRank >= 0) {
 | 
				
			||||||
        inventoryResponse.PlayerLevel = config.spoofMasteryRank;
 | 
					        inventoryResponse.PlayerLevel = config.spoofMasteryRank;
 | 
				
			||||||
        if (!xpBasedLevelCapDisabled) {
 | 
					        if (!xpBasedLevelCapDisabled) {
 | 
				
			||||||
@ -484,3 +495,21 @@ const getExpRequiredForMr = (rank: number): number => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return 2_250_000 + 147_500 * (rank - 30);
 | 
					    return 2_250_000 + 147_500 * (rank - 30);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const resourceInheritsFrom = (resourceName: string, targetName: string): boolean => {
 | 
				
			||||||
 | 
					    let parentName = resourceGetParent(resourceName);
 | 
				
			||||||
 | 
					    for (; parentName != undefined; parentName = resourceGetParent(parentName)) {
 | 
				
			||||||
 | 
					        if (parentName == targetName) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const resourceGetParent = (resourceName: string): string | undefined => {
 | 
				
			||||||
 | 
					    if (resourceName in ExportResources) {
 | 
				
			||||||
 | 
					        return ExportResources[resourceName].parentName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
				
			||||||
 | 
					    return ExportVirtuals[resourceName]?.parentName;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					import type { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { config, getReflexiveAddress } from "../../services/configService.ts";
 | 
					import { config } from "../../services/configService.ts";
 | 
				
			||||||
import { buildConfig } from "../../services/buildConfigService.ts";
 | 
					import { buildConfig } from "../../services/buildConfigService.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Account } from "../../models/loginModel.ts";
 | 
					import { Account } from "../../models/loginModel.ts";
 | 
				
			||||||
@ -20,7 +20,21 @@ export const loginController: RequestHandler = async (request, response) => {
 | 
				
			|||||||
            ? request.query.buildLabel.split(" ").join("+")
 | 
					            ? request.query.buildLabel.split(" ").join("+")
 | 
				
			||||||
            : buildConfig.buildLabel;
 | 
					            : buildConfig.buildLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { myAddress, myUrlBase } = getReflexiveAddress(request);
 | 
					    let myAddress: string;
 | 
				
			||||||
 | 
					    let myUrlBase: string = request.protocol + "://";
 | 
				
			||||||
 | 
					    if (request.host.indexOf("warframe.com") == -1) {
 | 
				
			||||||
 | 
					        // Client request was redirected cleanly, so we know it can reach us how it's reaching us now.
 | 
				
			||||||
 | 
					        myAddress = request.hostname;
 | 
				
			||||||
 | 
					        myUrlBase += request.host;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // Don't know how the client reached us, hoping the config does.
 | 
				
			||||||
 | 
					        myAddress = config.myAddress;
 | 
				
			||||||
 | 
					        myUrlBase += myAddress;
 | 
				
			||||||
 | 
					        const port: number = request.protocol == "http" ? config.httpPort || 80 : config.httpsPort || 443;
 | 
				
			||||||
 | 
					        if (port != (request.protocol == "http" ? 80 : 443)) {
 | 
				
			||||||
 | 
					            myUrlBase += ":" + port;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
        !account &&
 | 
					        !account &&
 | 
				
			||||||
@ -95,11 +109,11 @@ const createLoginResponse = (
 | 
				
			|||||||
        BuildLabel: buildLabel
 | 
					        BuildLabel: buildLabel
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    if (version_compare(buildLabel, "2015.02.13.10.41") >= 0) {
 | 
					    if (version_compare(buildLabel, "2015.02.13.10.41") >= 0) {
 | 
				
			||||||
        resp.NRS = [config.nrsAddress ?? myAddress];
 | 
					        resp.NRS = [myAddress];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (version_compare(buildLabel, "2015.05.14.16.29") >= 0) {
 | 
					    if (version_compare(buildLabel, "2015.05.14.16.29") >= 0) {
 | 
				
			||||||
        // U17 and up
 | 
					        // U17 and up
 | 
				
			||||||
        resp.IRC = [config.ircAddress ?? myAddress];
 | 
					        resp.IRC = config.myIrcAddresses ?? [myAddress];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (version_compare(buildLabel, "2018.11.08.14.45") >= 0) {
 | 
					    if (version_compare(buildLabel, "2018.11.08.14.45") >= 0) {
 | 
				
			||||||
        // U24 and up
 | 
					        // U24 and up
 | 
				
			||||||
 | 
				
			|||||||
@ -1,36 +0,0 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					 | 
				
			||||||
import { ExportResources, ExportVirtuals } from "warframe-public-export-plus";
 | 
					 | 
				
			||||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
					 | 
				
			||||||
import { addItem, getInventory } from "../../services/inventoryService.ts";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const unlockAllCapturaScenesController: RequestHandler = async (req, res) => {
 | 
					 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					 | 
				
			||||||
    const inventory = await getInventory(accountId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (const uniqueName of Object.keys(ExportResources)) {
 | 
					 | 
				
			||||||
        if (resourceInheritsFrom(uniqueName, "/Lotus/Types/Items/MiscItems/PhotoboothTile")) {
 | 
					 | 
				
			||||||
            await addItem(inventory, uniqueName, 1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    await inventory.save();
 | 
					 | 
				
			||||||
    res.end();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const resourceInheritsFrom = (resourceName: string, targetName: string): boolean => {
 | 
					 | 
				
			||||||
    let parentName = resourceGetParent(resourceName);
 | 
					 | 
				
			||||||
    for (; parentName != undefined; parentName = resourceGetParent(parentName)) {
 | 
					 | 
				
			||||||
        if (parentName == targetName) {
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const resourceGetParent = (resourceName: string): string | undefined => {
 | 
					 | 
				
			||||||
    if (resourceName in ExportResources) {
 | 
					 | 
				
			||||||
        return ExportResources[resourceName].parentName;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
					 | 
				
			||||||
    return ExportVirtuals[resourceName]?.parentName;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					 | 
				
			||||||
import allScans from "../../../static/fixed_responses/allScans.json" with { type: "json" };
 | 
					 | 
				
			||||||
import { ExportEnemies } from "warframe-public-export-plus";
 | 
					 | 
				
			||||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
					 | 
				
			||||||
import { getStats } from "../../services/statsService.ts";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const unlockAllScansController: RequestHandler = async (req, res) => {
 | 
					 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					 | 
				
			||||||
    const stats = await getStats(accountId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const scanTypes = new Set<string>(allScans);
 | 
					 | 
				
			||||||
    for (const type of Object.keys(ExportEnemies.avatars)) {
 | 
					 | 
				
			||||||
        scanTypes.add(type);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    stats.Scans = [];
 | 
					 | 
				
			||||||
    for (const type of scanTypes) {
 | 
					 | 
				
			||||||
        stats.Scans.push({ type, scans: 9999 });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    await stats.save();
 | 
					 | 
				
			||||||
    res.end();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,19 +0,0 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					 | 
				
			||||||
import allShipFeatures from "../../../static/fixed_responses/allShipFeatures.json" with { type: "json" };
 | 
					 | 
				
			||||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
					 | 
				
			||||||
import { getPersonalRooms } from "../../services/personalRoomsService.ts";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const unlockAllShipFeaturesController: RequestHandler = async (req, res) => {
 | 
					 | 
				
			||||||
    const accountId = await getAccountIdForRequest(req);
 | 
					 | 
				
			||||||
    const personalRooms = await getPersonalRooms(accountId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const featureSet = new Set(personalRooms.Ship.Features);
 | 
					 | 
				
			||||||
    for (const feature of allShipFeatures) {
 | 
					 | 
				
			||||||
        if (!featureSet.has(feature)) {
 | 
					 | 
				
			||||||
            personalRooms.Ship.Features.push(feature);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    await personalRooms.save();
 | 
					 | 
				
			||||||
    res.end();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,5 +1,8 @@
 | 
				
			|||||||
import type { RequestHandler } from "express";
 | 
					import type { RequestHandler } from "express";
 | 
				
			||||||
import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
					import { getAccountIdForRequest } from "../../services/loginService.ts";
 | 
				
			||||||
 | 
					import { config } from "../../services/configService.ts";
 | 
				
			||||||
 | 
					import allScans from "../../../static/fixed_responses/allScans.json" with { type: "json" };
 | 
				
			||||||
 | 
					import { ExportEnemies } from "warframe-public-export-plus";
 | 
				
			||||||
import { getInventory } from "../../services/inventoryService.ts";
 | 
					import { getInventory } from "../../services/inventoryService.ts";
 | 
				
			||||||
import { getStats } from "../../services/statsService.ts";
 | 
					import { getStats } from "../../services/statsService.ts";
 | 
				
			||||||
import type { IStatsClient } from "../../types/statTypes.ts";
 | 
					import type { IStatsClient } from "../../types/statTypes.ts";
 | 
				
			||||||
@ -9,7 +12,7 @@ const viewController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    const inventory = await getInventory(accountId, "XPInfo");
 | 
					    const inventory = await getInventory(accountId, "XPInfo");
 | 
				
			||||||
    const playerStats = await getStats(accountId);
 | 
					    const playerStats = await getStats(accountId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const responseJson = playerStats.toJSON<IStatsClient>();
 | 
					    const responseJson = playerStats.toJSON() as IStatsClient;
 | 
				
			||||||
    responseJson.Weapons ??= [];
 | 
					    responseJson.Weapons ??= [];
 | 
				
			||||||
    for (const item of inventory.XPInfo) {
 | 
					    for (const item of inventory.XPInfo) {
 | 
				
			||||||
        const weaponIndex = responseJson.Weapons.findIndex(element => element.type == item.ItemType);
 | 
					        const weaponIndex = responseJson.Weapons.findIndex(element => element.type == item.ItemType);
 | 
				
			||||||
@ -19,6 +22,24 @@ const viewController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
            responseJson.Weapons.push({ type: item.ItemType, xp: item.XP });
 | 
					            responseJson.Weapons.push({ type: item.ItemType, xp: item.XP });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (config.unlockAllScans) {
 | 
				
			||||||
 | 
					        const scans = new Set(allScans);
 | 
				
			||||||
 | 
					        for (const type of Object.keys(ExportEnemies.avatars)) {
 | 
				
			||||||
 | 
					            if (!scans.has(type)) scans.add(type);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Take any existing scans and also set them to 9999
 | 
				
			||||||
 | 
					        if (responseJson.Scans) {
 | 
				
			||||||
 | 
					            for (const scan of responseJson.Scans) {
 | 
				
			||||||
 | 
					                scans.add(scan.type);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        responseJson.Scans = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const type of scans) {
 | 
				
			||||||
 | 
					            responseJson.Scans.push({ type: type, scans: 9999 });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    res.json(responseJson);
 | 
					    res.json(responseJson);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,9 +16,6 @@ import { completeAllMissionsController } from "../controllers/custom/completeAll
 | 
				
			|||||||
import { addMissingHelminthBlueprintsController } from "../controllers/custom/addMissingHelminthBlueprintsController.ts";
 | 
					import { addMissingHelminthBlueprintsController } from "../controllers/custom/addMissingHelminthBlueprintsController.ts";
 | 
				
			||||||
import { unlockAllProfitTakerStagesController } from "../controllers/custom/unlockAllProfitTakerStagesController.ts";
 | 
					import { unlockAllProfitTakerStagesController } from "../controllers/custom/unlockAllProfitTakerStagesController.ts";
 | 
				
			||||||
import { unlockAllSimarisResearchEntriesController } from "../controllers/custom/unlockAllSimarisResearchEntriesController.ts";
 | 
					import { unlockAllSimarisResearchEntriesController } from "../controllers/custom/unlockAllSimarisResearchEntriesController.ts";
 | 
				
			||||||
import { unlockAllScansController } from "../controllers/custom/unlockAllScansController.ts";
 | 
					 | 
				
			||||||
import { unlockAllShipFeaturesController } from "../controllers/custom/unlockAllShipFeaturesController.ts";
 | 
					 | 
				
			||||||
import { unlockAllCapturaScenesController } from "../controllers/custom/unlockAllCapturaScenesController.ts";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { abilityOverrideController } from "../controllers/custom/abilityOverrideController.ts";
 | 
					import { abilityOverrideController } from "../controllers/custom/abilityOverrideController.ts";
 | 
				
			||||||
import { createAccountController } from "../controllers/custom/createAccountController.ts";
 | 
					import { createAccountController } from "../controllers/custom/createAccountController.ts";
 | 
				
			||||||
@ -55,9 +52,6 @@ customRouter.get("/completeAllMissions", completeAllMissionsController);
 | 
				
			|||||||
customRouter.get("/addMissingHelminthBlueprints", addMissingHelminthBlueprintsController);
 | 
					customRouter.get("/addMissingHelminthBlueprints", addMissingHelminthBlueprintsController);
 | 
				
			||||||
customRouter.get("/unlockAllProfitTakerStages", unlockAllProfitTakerStagesController);
 | 
					customRouter.get("/unlockAllProfitTakerStages", unlockAllProfitTakerStagesController);
 | 
				
			||||||
customRouter.get("/unlockAllSimarisResearchEntries", unlockAllSimarisResearchEntriesController);
 | 
					customRouter.get("/unlockAllSimarisResearchEntries", unlockAllSimarisResearchEntriesController);
 | 
				
			||||||
customRouter.get("/unlockAllScans", unlockAllScansController);
 | 
					 | 
				
			||||||
customRouter.get("/unlockAllShipFeatures", unlockAllShipFeaturesController);
 | 
					 | 
				
			||||||
customRouter.get("/unlockAllCapturaScenes", unlockAllCapturaScenesController);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
customRouter.post("/abilityOverride", abilityOverrideController);
 | 
					customRouter.post("/abilityOverride", abilityOverrideController);
 | 
				
			||||||
customRouter.post("/createAccount", createAccountController);
 | 
					customRouter.post("/createAccount", createAccountController);
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@ import path from "path";
 | 
				
			|||||||
import { repoDir } from "../helpers/pathHelper.ts";
 | 
					import { repoDir } from "../helpers/pathHelper.ts";
 | 
				
			||||||
import { args } from "../helpers/commandLineArguments.ts";
 | 
					import { args } from "../helpers/commandLineArguments.ts";
 | 
				
			||||||
import { Inbox } from "../models/inboxModel.ts";
 | 
					import { Inbox } from "../models/inboxModel.ts";
 | 
				
			||||||
import type { Request } from "express";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IConfig {
 | 
					export interface IConfig {
 | 
				
			||||||
    mongodbUrl: string;
 | 
					    mongodbUrl: string;
 | 
				
			||||||
@ -14,15 +13,16 @@ export interface IConfig {
 | 
				
			|||||||
    myAddress: string;
 | 
					    myAddress: string;
 | 
				
			||||||
    httpPort?: number;
 | 
					    httpPort?: number;
 | 
				
			||||||
    httpsPort?: number;
 | 
					    httpsPort?: number;
 | 
				
			||||||
    ircAddress?: string;
 | 
					    myIrcAddresses?: string[];
 | 
				
			||||||
    hubAddress?: string;
 | 
					 | 
				
			||||||
    nrsAddress?: string;
 | 
					 | 
				
			||||||
    administratorNames?: string[];
 | 
					    administratorNames?: string[];
 | 
				
			||||||
    autoCreateAccount?: boolean;
 | 
					    autoCreateAccount?: boolean;
 | 
				
			||||||
    skipTutorial?: boolean;
 | 
					    skipTutorial?: boolean;
 | 
				
			||||||
 | 
					    unlockAllScans?: boolean;
 | 
				
			||||||
 | 
					    unlockAllShipFeatures?: boolean;
 | 
				
			||||||
    unlockAllShipDecorations?: boolean;
 | 
					    unlockAllShipDecorations?: boolean;
 | 
				
			||||||
    unlockAllFlavourItems?: boolean;
 | 
					    unlockAllFlavourItems?: boolean;
 | 
				
			||||||
    unlockAllSkins?: boolean;
 | 
					    unlockAllSkins?: boolean;
 | 
				
			||||||
 | 
					    unlockAllCapturaScenes?: boolean;
 | 
				
			||||||
    unlockAllDecoRecipes?: boolean;
 | 
					    unlockAllDecoRecipes?: boolean;
 | 
				
			||||||
    fullyStockedVendors?: boolean;
 | 
					    fullyStockedVendors?: boolean;
 | 
				
			||||||
    skipClanKeyCrafting?: boolean;
 | 
					    skipClanKeyCrafting?: boolean;
 | 
				
			||||||
@ -86,7 +86,6 @@ export interface IConfig {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const configRemovedOptionsKeys = [
 | 
					export const configRemovedOptionsKeys = [
 | 
				
			||||||
    "NRS",
 | 
					    "NRS",
 | 
				
			||||||
    "myIrcAddresses",
 | 
					 | 
				
			||||||
    "skipAllDialogue",
 | 
					    "skipAllDialogue",
 | 
				
			||||||
    "infiniteCredits",
 | 
					    "infiniteCredits",
 | 
				
			||||||
    "infinitePlatinum",
 | 
					    "infinitePlatinum",
 | 
				
			||||||
@ -106,9 +105,6 @@ export const configRemovedOptionsKeys = [
 | 
				
			|||||||
    "unlockArcanesEverywhere",
 | 
					    "unlockArcanesEverywhere",
 | 
				
			||||||
    "unlockAllProfitTakerStages",
 | 
					    "unlockAllProfitTakerStages",
 | 
				
			||||||
    "unlockAllSimarisResearchEntries",
 | 
					    "unlockAllSimarisResearchEntries",
 | 
				
			||||||
    "unlockAllScans",
 | 
					 | 
				
			||||||
    "unlockAllShipFeatures",
 | 
					 | 
				
			||||||
    "unlockAllCapturaScenes",
 | 
					 | 
				
			||||||
    "noDailyStandingLimits",
 | 
					    "noDailyStandingLimits",
 | 
				
			||||||
    "noDailyFocusLimit",
 | 
					    "noDailyFocusLimit",
 | 
				
			||||||
    "noArgonCrystalDecay",
 | 
					    "noArgonCrystalDecay",
 | 
				
			||||||
@ -169,22 +165,3 @@ export const syncConfigWithDatabase = (): void => {
 | 
				
			|||||||
        void Inbox.deleteMany({ goalTag: "GalleonRobbery" }).then(() => {});
 | 
					        void Inbox.deleteMany({ goalTag: "GalleonRobbery" }).then(() => {});
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
export const getReflexiveAddress = (request: Request): { myAddress: string; myUrlBase: string } => {
 | 
					 | 
				
			||||||
    let myAddress: string;
 | 
					 | 
				
			||||||
    let myUrlBase: string = request.protocol + "://";
 | 
					 | 
				
			||||||
    if (request.host.indexOf("warframe.com") == -1) {
 | 
					 | 
				
			||||||
        // Client request was redirected cleanly, so we know it can reach us how it's reaching us now.
 | 
					 | 
				
			||||||
        myAddress = request.hostname;
 | 
					 | 
				
			||||||
        myUrlBase += request.host;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        // Don't know how the client reached us, hoping the config does.
 | 
					 | 
				
			||||||
        myAddress = config.myAddress;
 | 
					 | 
				
			||||||
        myUrlBase += myAddress;
 | 
					 | 
				
			||||||
        const port: number = request.protocol == "http" ? config.httpPort || 80 : config.httpsPort || 443;
 | 
					 | 
				
			||||||
        if (port != (request.protocol == "http" ? 80 : 443)) {
 | 
					 | 
				
			||||||
            myUrlBase += ":" + port;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return { myAddress, myUrlBase };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -209,7 +209,7 @@ export const handlePurchase = async (
 | 
				
			|||||||
        inventory,
 | 
					        inventory,
 | 
				
			||||||
        purchaseRequest.PurchaseParams.ExpectedPrice,
 | 
					        purchaseRequest.PurchaseParams.ExpectedPrice,
 | 
				
			||||||
        purchaseRequest.PurchaseParams.UsePremium,
 | 
					        purchaseRequest.PurchaseParams.UsePremium,
 | 
				
			||||||
        purchaseResponse.InventoryChanges
 | 
					        prePurchaseInventoryChanges
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (purchaseRequest.PurchaseParams.Source) {
 | 
					    switch (purchaseRequest.PurchaseParams.Source) {
 | 
				
			||||||
 | 
				
			|||||||
@ -808,11 +808,8 @@
 | 
				
			|||||||
                                    <label class="form-check-label" for="finishInvasionsInOneMission" data-loc="cheats_finishInvasionsInOneMission"></label>
 | 
					                                    <label class="form-check-label" for="finishInvasionsInOneMission" data-loc="cheats_finishInvasionsInOneMission"></label>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
                                <div class="mt-2 mb-2 d-flex flex-wrap gap-2">
 | 
					                                <div class="mt-2 mb-2 d-flex flex-wrap gap-2">
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(doUnlockAllShipFeatures);" data-loc="cheats_unlockAllShipFeatures"></button>
 | 
					 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(unlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(unlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(markAllAsRead);" data-loc="cheats_markAllAsRead"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(markAllAsRead);" data-loc="cheats_markAllAsRead"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(doUnlockAllScans);" data-loc="cheats_unlockAllScans"></button>
 | 
					 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(doUnlockAllCapturaScenes);" data-loc="cheats_unlockAllCapturaScenes"></button>
 | 
					 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="doUnlockAllFocusSchools();" data-loc="cheats_unlockAllFocusSchools"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doUnlockAllFocusSchools();" data-loc="cheats_unlockAllFocusSchools"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(addMissingHelminthRecipes);" data-loc="cheats_addMissingSubsumedAbilities"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(addMissingHelminthRecipes);" data-loc="cheats_addMissingSubsumedAbilities"></button>
 | 
				
			||||||
@ -843,6 +840,14 @@
 | 
				
			|||||||
                                        <input class="form-check-input" type="checkbox" id="skipTutorial" />
 | 
					                                        <input class="form-check-input" type="checkbox" id="skipTutorial" />
 | 
				
			||||||
                                        <label class="form-check-label" for="skipTutorial" data-loc="cheats_skipTutorial"></label>
 | 
					                                        <label class="form-check-label" for="skipTutorial" data-loc="cheats_skipTutorial"></label>
 | 
				
			||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                    <div class="form-check">
 | 
				
			||||||
 | 
					                                        <input class="form-check-input" type="checkbox" id="unlockAllScans" />
 | 
				
			||||||
 | 
					                                        <label class="form-check-label" for="unlockAllScans" data-loc="cheats_unlockAllScans"></label>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                    <div class="form-check">
 | 
				
			||||||
 | 
					                                        <input class="form-check-input" type="checkbox" id="unlockAllShipFeatures" />
 | 
				
			||||||
 | 
					                                        <label class="form-check-label" for="unlockAllShipFeatures" data-loc="cheats_unlockAllShipFeatures"></label>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
                                    <div class="form-check">
 | 
					                                    <div class="form-check">
 | 
				
			||||||
                                        <input class="form-check-input" type="checkbox" id="unlockAllShipDecorations" />
 | 
					                                        <input class="form-check-input" type="checkbox" id="unlockAllShipDecorations" />
 | 
				
			||||||
                                        <label class="form-check-label" for="unlockAllShipDecorations" data-loc="cheats_unlockAllShipDecorations"></label>
 | 
					                                        <label class="form-check-label" for="unlockAllShipDecorations" data-loc="cheats_unlockAllShipDecorations"></label>
 | 
				
			||||||
@ -855,6 +860,10 @@
 | 
				
			|||||||
                                        <input class="form-check-input" type="checkbox" id="unlockAllSkins" />
 | 
					                                        <input class="form-check-input" type="checkbox" id="unlockAllSkins" />
 | 
				
			||||||
                                        <label class="form-check-label" for="unlockAllSkins" data-loc="cheats_unlockAllSkins"></label>
 | 
					                                        <label class="form-check-label" for="unlockAllSkins" data-loc="cheats_unlockAllSkins"></label>
 | 
				
			||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                    <div class="form-check">
 | 
				
			||||||
 | 
					                                        <input class="form-check-input" type="checkbox" id="unlockAllCapturaScenes" />
 | 
				
			||||||
 | 
					                                        <label class="form-check-label" for="unlockAllCapturaScenes" data-loc="cheats_unlockAllCapturaScenes"></label>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
                                    <div class="form-check">
 | 
					                                    <div class="form-check">
 | 
				
			||||||
                                        <input class="form-check-input" type="checkbox" id="unlockAllDecoRecipes" />
 | 
					                                        <input class="form-check-input" type="checkbox" id="unlockAllDecoRecipes" />
 | 
				
			||||||
                                        <label class="form-check-label" for="unlockAllDecoRecipes" data-loc="cheats_unlockAllDecoRecipes"></label>
 | 
					                                        <label class="form-check-label" for="unlockAllDecoRecipes" data-loc="cheats_unlockAllDecoRecipes"></label>
 | 
				
			||||||
 | 
				
			|||||||
@ -2339,7 +2339,7 @@ function unlockFocusSchool(upgradeType) {
 | 
				
			|||||||
        $.post({
 | 
					        $.post({
 | 
				
			||||||
            url: "/api/focus.php?" + window.authz + "&op=5",
 | 
					            url: "/api/focus.php?" + window.authz + "&op=5",
 | 
				
			||||||
            contentType: "text/plain",
 | 
					            contentType: "text/plain",
 | 
				
			||||||
            data: JSON.stringify({ FocusType: null })
 | 
					            data: "{}"
 | 
				
			||||||
        }).done(function () {
 | 
					        }).done(function () {
 | 
				
			||||||
            // Unlock the way now
 | 
					            // Unlock the way now
 | 
				
			||||||
            $.post({
 | 
					            $.post({
 | 
				
			||||||
@ -2750,24 +2750,6 @@ async function doMaxPlexus() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function doUnlockAllScans() {
 | 
					 | 
				
			||||||
    await revalidateAuthz();
 | 
					 | 
				
			||||||
    await fetch("/custom/unlockAllScans?" + window.authz);
 | 
					 | 
				
			||||||
    toast(loc("cheats_unlockSuccRelog"));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function doUnlockAllShipFeatures() {
 | 
					 | 
				
			||||||
    await revalidateAuthz();
 | 
					 | 
				
			||||||
    await fetch("/custom/unlockAllShipFeatures?" + window.authz);
 | 
					 | 
				
			||||||
    toast(loc("cheats_unlockSuccInventory"));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function doUnlockAllCapturaScenes() {
 | 
					 | 
				
			||||||
    await revalidateAuthz();
 | 
					 | 
				
			||||||
    await fetch("/custom/unlockAllCapturaScenes?" + window.authz);
 | 
					 | 
				
			||||||
    toast(loc("cheats_unlockSuccInventory"));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function unlockAllMissions() {
 | 
					async function unlockAllMissions() {
 | 
				
			||||||
    await revalidateAuthz();
 | 
					    await revalidateAuthz();
 | 
				
			||||||
    await fetch("/custom/completeAllMissions?" + window.authz);
 | 
					    await fetch("/custom/completeAllMissions?" + window.authz);
 | 
				
			||||||
@ -2777,13 +2759,13 @@ async function unlockAllMissions() {
 | 
				
			|||||||
async function unlockAllProfitTakerStages() {
 | 
					async function unlockAllProfitTakerStages() {
 | 
				
			||||||
    await revalidateAuthz();
 | 
					    await revalidateAuthz();
 | 
				
			||||||
    await fetch("/custom/unlockAllProfitTakerStages?" + window.authz);
 | 
					    await fetch("/custom/unlockAllProfitTakerStages?" + window.authz);
 | 
				
			||||||
    toast(loc("cheats_unlockSuccInventory"));
 | 
					    toast(loc("cheats_unlockSucc"));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function unlockAllSimarisResearchEntries() {
 | 
					async function unlockAllSimarisResearchEntries() {
 | 
				
			||||||
    await revalidateAuthz();
 | 
					    await revalidateAuthz();
 | 
				
			||||||
    await fetch("/custom/unlockAllSimarisResearchEntries?" + window.authz);
 | 
					    await fetch("/custom/unlockAllSimarisResearchEntries?" + window.authz);
 | 
				
			||||||
    toast(loc("cheats_unlockSuccInventory"));
 | 
					    toast(loc("cheats_unlockSucc"));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const importSamples = {
 | 
					const importSamples = {
 | 
				
			||||||
 | 
				
			|||||||
@ -171,12 +171,11 @@ dict = {
 | 
				
			|||||||
    mods_addMissingUnrankedMods: `Fehlende Mods ohne Rang hinzufügen`,
 | 
					    mods_addMissingUnrankedMods: `Fehlende Mods ohne Rang hinzufügen`,
 | 
				
			||||||
    mods_removeUnranked: `Mods ohne Rang entfernen`,
 | 
					    mods_removeUnranked: `Mods ohne Rang entfernen`,
 | 
				
			||||||
    mods_addMissingMaxRankMods: `Fehlende Mods mit Max. Rang hinzufügen`,
 | 
					    mods_addMissingMaxRankMods: `Fehlende Mods mit Max. Rang hinzufügen`,
 | 
				
			||||||
    cheats_administratorRequirement: `Du musst Administrator sein, um diese Funktion nutzen zu können. Um Administrator zu werden, füge <code>"|DISPLAYNAME|"</code> zu <code>administratorNames</code> in der config.json hinzu.`,
 | 
					    cheats_administratorRequirement: `Du musst Administrator sein, um diese Funktion nutzen zu können. Um Administrator zu werden, füge <code>|DISPLAYNAME|</code> zu <code>administratorNames</code> in der config.json hinzu.`,
 | 
				
			||||||
    cheats_server: `Server`,
 | 
					    cheats_server: `Server`,
 | 
				
			||||||
    cheats_skipTutorial: `Tutorial überspringen`,
 | 
					    cheats_skipTutorial: `Tutorial überspringen`,
 | 
				
			||||||
    cheats_skipAllDialogue: `Alle Dialoge überspringen`,
 | 
					    cheats_skipAllDialogue: `Alle Dialoge überspringen`,
 | 
				
			||||||
    cheats_unlockAllScans: `Alle Scans freischalten`,
 | 
					    cheats_unlockAllScans: `Alle Scans freischalten`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					 | 
				
			||||||
    cheats_unlockAllMissions: `Alle Missionen freischalten`,
 | 
					    cheats_unlockAllMissions: `Alle Missionen freischalten`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `Erfolgreich. Bitte beachte, dass du ein Dojo/Relais besuchen oder dich neu einloggen musst, damit die Sternenkarte aktualisiert wird.`,
 | 
					    cheats_unlockAllMissions_ok: `Erfolgreich. Bitte beachte, dass du ein Dojo/Relais besuchen oder dich neu einloggen musst, damit die Sternenkarte aktualisiert wird.`,
 | 
				
			||||||
    cheats_infiniteCredits: `Unendlich Credits`,
 | 
					    cheats_infiniteCredits: `Unendlich Credits`,
 | 
				
			||||||
@ -213,7 +212,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `Baro hat volles Inventar`,
 | 
					    cheats_baroFullyStocked: `Baro hat volles Inventar`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
 | 
					    cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Alle Profiteintreiber-Phasen freischalten`,
 | 
					    cheats_unlockAllProfitTakerStages: `Alle Profiteintreiber-Phasen freischalten`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSucc: `[UNTRANSLATED] Successfully unlocked.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Riven-Mod Herausforderung sofort abschließen`,
 | 
					    cheats_instantFinishRivenChallenge: `Riven-Mod Herausforderung sofort abschließen`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`,
 | 
					    cheats_instantResourceExtractorDrones: `Sofortige Ressourcen-Extraktor-Drohnen`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`,
 | 
					    cheats_noResourceExtractorDronesDamage: `Kein Schaden für Ressourcen-Extraktor-Drohnen`,
 | 
				
			||||||
 | 
				
			|||||||
@ -170,12 +170,11 @@ dict = {
 | 
				
			|||||||
    mods_addMissingUnrankedMods: `Add Missing Unranked Mods`,
 | 
					    mods_addMissingUnrankedMods: `Add Missing Unranked Mods`,
 | 
				
			||||||
    mods_removeUnranked: `Remove Unranked Mods`,
 | 
					    mods_removeUnranked: `Remove Unranked Mods`,
 | 
				
			||||||
    mods_addMissingMaxRankMods: `Add Missing Max Rank Mods`,
 | 
					    mods_addMissingMaxRankMods: `Add Missing Max Rank Mods`,
 | 
				
			||||||
    cheats_administratorRequirement: `You must be an administrator to use this feature. To become an administrator, add <code>"|DISPLAYNAME|"</code> to <code>administratorNames</code> in the config.json.`,
 | 
					    cheats_administratorRequirement: `You must be an administrator to use this feature. To become an administrator, add <code>|DISPLAYNAME|</code> to <code>administratorNames</code> in the config.json.`,
 | 
				
			||||||
    cheats_server: `Server`,
 | 
					    cheats_server: `Server`,
 | 
				
			||||||
    cheats_skipTutorial: `Skip Tutorial`,
 | 
					    cheats_skipTutorial: `Skip Tutorial`,
 | 
				
			||||||
    cheats_skipAllDialogue: `Skip All Dialogue`,
 | 
					    cheats_skipAllDialogue: `Skip All Dialogue`,
 | 
				
			||||||
    cheats_unlockAllScans: `Unlock All Scans`,
 | 
					    cheats_unlockAllScans: `Unlock All Scans`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					 | 
				
			||||||
    cheats_unlockAllMissions: `Unlock All Missions`,
 | 
					    cheats_unlockAllMissions: `Unlock All Missions`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `Success. Please note that you'll need to enter a dojo/relay or relog for the client to refresh the star chart.`,
 | 
					    cheats_unlockAllMissions_ok: `Success. Please note that you'll need to enter a dojo/relay or relog for the client to refresh the star chart.`,
 | 
				
			||||||
    cheats_infiniteCredits: `Infinite Credits`,
 | 
					    cheats_infiniteCredits: `Infinite Credits`,
 | 
				
			||||||
@ -212,7 +211,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `Baro Fully Stocked`,
 | 
					    cheats_baroFullyStocked: `Baro Fully Stocked`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Syndicate Missions Repeatable`,
 | 
					    cheats_syndicateMissionsRepeatable: `Syndicate Missions Repeatable`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Unlock All Profit Taker Stages`,
 | 
					    cheats_unlockAllProfitTakerStages: `Unlock All Profit Taker Stages`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSucc: `Successfully unlocked.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Instant Finish Riven Challenge`,
 | 
					    cheats_instantFinishRivenChallenge: `Instant Finish Riven Challenge`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
 | 
					    cheats_instantResourceExtractorDrones: `Instant Resource Extractor Drones`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `No Resource Extractor Drones Damage`,
 | 
					    cheats_noResourceExtractorDronesDamage: `No Resource Extractor Drones Damage`,
 | 
				
			||||||
 | 
				
			|||||||
@ -79,8 +79,8 @@ dict = {
 | 
				
			|||||||
    navbar_cheats: `Trucos`,
 | 
					    navbar_cheats: `Trucos`,
 | 
				
			||||||
    navbar_import: `Importar`,
 | 
					    navbar_import: `Importar`,
 | 
				
			||||||
    inventory_addItems: `Agregar objetos`,
 | 
					    inventory_addItems: `Agregar objetos`,
 | 
				
			||||||
    inventory_addItemByItemType: `Sin refinar`,
 | 
					    inventory_addItemByItemType: `[UNTRANSLATED] Raw`,
 | 
				
			||||||
    inventory_addItemByItemType_warning: `Usa esta función bajo tu propio riesgo. Podría dañar tu inventario, y tendrías que eliminar los objetos manualmente si algo sale mal.`,
 | 
					    inventory_addItemByItemType_warning: `[UNTRANSLATED] Use this feature at your own risk. It may break your inventory, and you will need to remove items manually if something goes wrong.`,
 | 
				
			||||||
    inventory_suits: `Warframes`,
 | 
					    inventory_suits: `Warframes`,
 | 
				
			||||||
    inventory_longGuns: `Armas primarias`,
 | 
					    inventory_longGuns: `Armas primarias`,
 | 
				
			||||||
    inventory_pistols: `Armas secundarias`,
 | 
					    inventory_pistols: `Armas secundarias`,
 | 
				
			||||||
@ -171,12 +171,11 @@ dict = {
 | 
				
			|||||||
    mods_addMissingUnrankedMods: `Agregar mods sin rango faltantes`,
 | 
					    mods_addMissingUnrankedMods: `Agregar mods sin rango faltantes`,
 | 
				
			||||||
    mods_removeUnranked: `Quitar mods sin rango`,
 | 
					    mods_removeUnranked: `Quitar mods sin rango`,
 | 
				
			||||||
    mods_addMissingMaxRankMods: `Agregar mods de rango máximo faltantes`,
 | 
					    mods_addMissingMaxRankMods: `Agregar mods de rango máximo faltantes`,
 | 
				
			||||||
    cheats_administratorRequirement: `Debes ser administrador para usar esta función. Para convertirte en administrador, agrega <code>"|DISPLAYNAME|"</code> a <code>administratorNames</code> en el archivo config.json.`,
 | 
					    cheats_administratorRequirement: `Debes ser administrador para usar esta función. Para convertirte en administrador, agrega <code>|DISPLAYNAME|</code> a <code>administratorNames</code> en el archivo config.json.`,
 | 
				
			||||||
    cheats_server: `Servidor`,
 | 
					    cheats_server: `Servidor`,
 | 
				
			||||||
    cheats_skipTutorial: `Omitir tutorial`,
 | 
					    cheats_skipTutorial: `Omitir tutorial`,
 | 
				
			||||||
    cheats_skipAllDialogue: `Omitir todos los diálogos`,
 | 
					    cheats_skipAllDialogue: `Omitir todos los diálogos`,
 | 
				
			||||||
    cheats_unlockAllScans: `Desbloquear todos los escaneos`,
 | 
					    cheats_unlockAllScans: `Desbloquear todos los escaneos`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `Éxito. Ten en cuenta que deberás volver a iniciar sesión para que el cliente se actualice.`,
 | 
					 | 
				
			||||||
    cheats_unlockAllMissions: `Desbloquear todas las misiones`,
 | 
					    cheats_unlockAllMissions: `Desbloquear todas las misiones`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `Éxito. Ten en cuenta que deberás entrar a un dojo, repetidor o volver a iniciar sesión para que el cliente actualice el mapa estelar.`,
 | 
					    cheats_unlockAllMissions_ok: `Éxito. Ten en cuenta que deberás entrar a un dojo, repetidor o volver a iniciar sesión para que el cliente actualice el mapa estelar.`,
 | 
				
			||||||
    cheats_infiniteCredits: `Créditos infinitos`,
 | 
					    cheats_infiniteCredits: `Créditos infinitos`,
 | 
				
			||||||
@ -213,7 +212,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `Baro con stock completo`,
 | 
					    cheats_baroFullyStocked: `Baro con stock completo`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Misiones de sindicato rejugables`,
 | 
					    cheats_syndicateMissionsRepeatable: `Misiones de sindicato rejugables`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Desbloquea todas las etapas del Roba-ganancias`,
 | 
					    cheats_unlockAllProfitTakerStages: `Desbloquea todas las etapas del Roba-ganancias`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `Éxito. Ten en cuenta que deberás volver a sincronizar tu inventario. Para hacerlo, puedes usar el comando /sync en el Bootstrapper, visitar un dojo o repetidor, o volver a iniciar sesión.`,
 | 
					    cheats_unlockSucc: `[UNTRANSLATED] Successfully unlocked.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Terminar desafío de agrietado inmediatamente`,
 | 
					    cheats_instantFinishRivenChallenge: `Terminar desafío de agrietado inmediatamente`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `Drones de extracción de recursos instantáneos`,
 | 
					    cheats_instantResourceExtractorDrones: `Drones de extracción de recursos instantáneos`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `Sin daño a los drones extractores de recursos`,
 | 
					    cheats_noResourceExtractorDronesDamage: `Sin daño a los drones extractores de recursos`,
 | 
				
			||||||
@ -242,7 +241,7 @@ dict = {
 | 
				
			|||||||
    cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
 | 
					    cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
 | 
				
			||||||
    cheats_changeButton: `Cambiar`,
 | 
					    cheats_changeButton: `Cambiar`,
 | 
				
			||||||
    cheats_markAllAsRead: `Marcar bandeja de entrada como leída`,
 | 
					    cheats_markAllAsRead: `Marcar bandeja de entrada como leída`,
 | 
				
			||||||
    cheats_finishInvasionsInOneMission: `Finaliza Invasión en una mision`,
 | 
					    cheats_finishInvasionsInOneMission: `[UNTRANSLATED] Finish Invasions in One Mission`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    worldState: `Estado del mundo`,
 | 
					    worldState: `Estado del mundo`,
 | 
				
			||||||
    worldState_creditBoost: `Potenciador de Créditos`,
 | 
					    worldState_creditBoost: `Potenciador de Créditos`,
 | 
				
			||||||
 | 
				
			|||||||
@ -171,12 +171,11 @@ dict = {
 | 
				
			|||||||
    mods_addMissingUnrankedMods: `Ajouter les mods sans rang manquants`,
 | 
					    mods_addMissingUnrankedMods: `Ajouter les mods sans rang manquants`,
 | 
				
			||||||
    mods_removeUnranked: `Retirer les mods sans rang`,
 | 
					    mods_removeUnranked: `Retirer les mods sans rang`,
 | 
				
			||||||
    mods_addMissingMaxRankMods: `Ajouter les mods niveau max manquants`,
 | 
					    mods_addMissingMaxRankMods: `Ajouter les mods niveau max manquants`,
 | 
				
			||||||
    cheats_administratorRequirement: `Rôle d'administrateur requis pour cette fonctionnalité. Ajoutez <code>"|DISPLAYNAME|"</code> à la ligne <code>administratorNames</code> dans le fichier config.json.`,
 | 
					    cheats_administratorRequirement: `Rôle d'administrateur requis pour cette fonctionnalité. Ajoutez <code>|DISPLAYNAME|</code> à la ligne <code>administratorNames</code> dans le fichier config.json.`,
 | 
				
			||||||
    cheats_server: `Serveur`,
 | 
					    cheats_server: `Serveur`,
 | 
				
			||||||
    cheats_skipTutorial: `Passer le tutoriel`,
 | 
					    cheats_skipTutorial: `Passer le tutoriel`,
 | 
				
			||||||
    cheats_skipAllDialogue: `Passer les dialogues`,
 | 
					    cheats_skipAllDialogue: `Passer les dialogues`,
 | 
				
			||||||
    cheats_unlockAllScans: `Débloquer tous les scans`,
 | 
					    cheats_unlockAllScans: `Débloquer tous les scans`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					 | 
				
			||||||
    cheats_unlockAllMissions: `Débloquer toutes les missions`,
 | 
					    cheats_unlockAllMissions: `Débloquer toutes les missions`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `Succès. Une actualisation de l'inventaire est nécessaire.`,
 | 
					    cheats_unlockAllMissions_ok: `Succès. Une actualisation de l'inventaire est nécessaire.`,
 | 
				
			||||||
    cheats_infiniteCredits: `Crédits infinis`,
 | 
					    cheats_infiniteCredits: `Crédits infinis`,
 | 
				
			||||||
@ -213,7 +212,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `Stock de Baro au max`,
 | 
					    cheats_baroFullyStocked: `Stock de Baro au max`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Mission syndicat répétables`,
 | 
					    cheats_syndicateMissionsRepeatable: `Mission syndicat répétables`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Débloquer toutes les étapes du Preneur de Profit`,
 | 
					    cheats_unlockAllProfitTakerStages: `Débloquer toutes les étapes du Preneur de Profit`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSucc: `[UNTRANSLATED] Successfully unlocked.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Débloquer le challenge Riven instantanément`,
 | 
					    cheats_instantFinishRivenChallenge: `Débloquer le challenge Riven instantanément`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `Ressources de drones d'extraction instantannées`,
 | 
					    cheats_instantResourceExtractorDrones: `Ressources de drones d'extraction instantannées`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `Aucun dégâts aux drones d'extraction de resources`,
 | 
					    cheats_noResourceExtractorDronesDamage: `Aucun dégâts aux drones d'extraction de resources`,
 | 
				
			||||||
 | 
				
			|||||||
@ -171,12 +171,11 @@ dict = {
 | 
				
			|||||||
    mods_addMissingUnrankedMods: `Добавить недостающие моды без ранга`,
 | 
					    mods_addMissingUnrankedMods: `Добавить недостающие моды без ранга`,
 | 
				
			||||||
    mods_removeUnranked: `Удалить моды без ранга`,
 | 
					    mods_removeUnranked: `Удалить моды без ранга`,
 | 
				
			||||||
    mods_addMissingMaxRankMods: `Добавить недостающие моды макс. ранга`,
 | 
					    mods_addMissingMaxRankMods: `Добавить недостающие моды макс. ранга`,
 | 
				
			||||||
    cheats_administratorRequirement: `Вы должны быть администратором для использования этой функции. Чтобы стать администратором, добавьте <code>"|DISPLAYNAME|"</code> в <code>administratorNames</code> в config.json.`,
 | 
					    cheats_administratorRequirement: `Вы должны быть администратором для использования этой функции. Чтобы стать администратором, добавьте <code>\"|DISPLAYNAME|\"</code> в <code>administratorNames</code> в config.json.`,
 | 
				
			||||||
    cheats_server: `Сервер`,
 | 
					    cheats_server: `Сервер`,
 | 
				
			||||||
    cheats_skipTutorial: `Пропустить обучение`,
 | 
					    cheats_skipTutorial: `Пропустить обучение`,
 | 
				
			||||||
    cheats_skipAllDialogue: `Пропустить все диалоги`,
 | 
					    cheats_skipAllDialogue: `Пропустить все диалоги`,
 | 
				
			||||||
    cheats_unlockAllScans: `Разблокировать все сканирования`,
 | 
					    cheats_unlockAllScans: `Разблокировать все сканирования`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					 | 
				
			||||||
    cheats_unlockAllMissions: `Разблокировать все миссии`,
 | 
					    cheats_unlockAllMissions: `Разблокировать все миссии`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `Успех. Пожалуйста, обратите внимание, что вам нужно будет войти в Додзё/Реле или перезайти, чтобы клиент обновил звездную карту.`,
 | 
					    cheats_unlockAllMissions_ok: `Успех. Пожалуйста, обратите внимание, что вам нужно будет войти в Додзё/Реле или перезайти, чтобы клиент обновил звездную карту.`,
 | 
				
			||||||
    cheats_infiniteCredits: `Бесконечные Кредиты`,
 | 
					    cheats_infiniteCredits: `Бесконечные Кредиты`,
 | 
				
			||||||
@ -213,7 +212,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `Баро полностью укомплектован`,
 | 
					    cheats_baroFullyStocked: `Баро полностью укомплектован`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Повторять миссии синдиката`,
 | 
					    cheats_syndicateMissionsRepeatable: `Повторять миссии синдиката`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Разблокировать все этапы Сферы извлечения прибыли`,
 | 
					    cheats_unlockAllProfitTakerStages: `Разблокировать все этапы Сферы извлечения прибыли`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSucc: `Успешно разблокировано.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Мгновенное завершение испытания мода Разлома`,
 | 
					    cheats_instantFinishRivenChallenge: `Мгновенное завершение испытания мода Разлома`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `Мгновенно добывающие Дроны-сборщики`,
 | 
					    cheats_instantResourceExtractorDrones: `Мгновенно добывающие Дроны-сборщики`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `Без урона по Дронам-сборщикам`,
 | 
					    cheats_noResourceExtractorDronesDamage: `Без урона по Дронам-сборщикам`,
 | 
				
			||||||
 | 
				
			|||||||
@ -171,12 +171,11 @@ dict = {
 | 
				
			|||||||
    mods_addMissingUnrankedMods: `Добавити недостаючі модифікатори без рівня`,
 | 
					    mods_addMissingUnrankedMods: `Добавити недостаючі модифікатори без рівня`,
 | 
				
			||||||
    mods_removeUnranked: `Видалити модифікатори без рівня`,
 | 
					    mods_removeUnranked: `Видалити модифікатори без рівня`,
 | 
				
			||||||
    mods_addMissingMaxRankMods: `Добавити недостаючі модифікатори макс. рівня`,
 | 
					    mods_addMissingMaxRankMods: `Добавити недостаючі модифікатори макс. рівня`,
 | 
				
			||||||
    cheats_administratorRequirement: `Ви повинні бути адміністратором для використання цієї функції. Щоб стати адміністратором, додайте <code>"|DISPLAYNAME|"</code> в <code>administratorNames</code> в config.json.`,
 | 
					    cheats_administratorRequirement: `Ви повинні бути адміністратором для використання цієї функції. Щоб стати адміністратором, додайте <code>\"|DISPLAYNAME|\"</code> в <code>administratorNames</code> в config.json.`,
 | 
				
			||||||
    cheats_server: `Сервер`,
 | 
					    cheats_server: `Сервер`,
 | 
				
			||||||
    cheats_skipTutorial: `Пропустити навчання`,
 | 
					    cheats_skipTutorial: `Пропустити навчання`,
 | 
				
			||||||
    cheats_skipAllDialogue: `Пропустити всі діалоги`,
 | 
					    cheats_skipAllDialogue: `Пропустити всі діалоги`,
 | 
				
			||||||
    cheats_unlockAllScans: `Розблокувати всі сканування`,
 | 
					    cheats_unlockAllScans: `Розблокувати всі сканування`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					 | 
				
			||||||
    cheats_unlockAllMissions: `Розблокувати всі місії`,
 | 
					    cheats_unlockAllMissions: `Розблокувати всі місії`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `Успіх. Будь ласка, зверніть увагу, що вам потрібно буде увійти в Доджьо/Реле або перезайти, щоб клієнт оновив Зоряну мапу.`,
 | 
					    cheats_unlockAllMissions_ok: `Успіх. Будь ласка, зверніть увагу, що вам потрібно буде увійти в Доджьо/Реле або перезайти, щоб клієнт оновив Зоряну мапу.`,
 | 
				
			||||||
    cheats_infiniteCredits: `Бескінечні Кредити`,
 | 
					    cheats_infiniteCredits: `Бескінечні Кредити`,
 | 
				
			||||||
@ -213,7 +212,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `Баро повністю укомплектований`,
 | 
					    cheats_baroFullyStocked: `Баро повністю укомплектований`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Повторювати місії синдиката`,
 | 
					    cheats_syndicateMissionsRepeatable: `Повторювати місії синдиката`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Розблокувати всі етапи Привласнювачки`,
 | 
					    cheats_unlockAllProfitTakerStages: `Розблокувати всі етапи Привласнювачки`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSucc: `Успішно розблоковано.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Миттєве завершення випробування модифікатора Розколу`,
 | 
					    cheats_instantFinishRivenChallenge: `Миттєве завершення випробування модифікатора Розколу`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `Миттєво добуваючі Дрони-видобувачі`,
 | 
					    cheats_instantResourceExtractorDrones: `Миттєво добуваючі Дрони-видобувачі`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `Без шкоди по Дронам-видобувачам`,
 | 
					    cheats_noResourceExtractorDronesDamage: `Без шкоди по Дронам-видобувачам`,
 | 
				
			||||||
 | 
				
			|||||||
@ -171,12 +171,11 @@ dict = {
 | 
				
			|||||||
    mods_addMissingUnrankedMods: `添加所有缺失的Mods`,
 | 
					    mods_addMissingUnrankedMods: `添加所有缺失的Mods`,
 | 
				
			||||||
    mods_removeUnranked: `删除所有未升级的Mods`,
 | 
					    mods_removeUnranked: `删除所有未升级的Mods`,
 | 
				
			||||||
    mods_addMissingMaxRankMods: `添加所有缺失的满级Mods`,
 | 
					    mods_addMissingMaxRankMods: `添加所有缺失的满级Mods`,
 | 
				
			||||||
    cheats_administratorRequirement: `您必须是管理员才能使用此功能.要成为管理员,请在 config.json 中将 <code>"|DISPLAYNAME|"</code> 添加到 <code>administratorNames</code>`,
 | 
					    cheats_administratorRequirement: `您必须是管理员才能使用此功能.要成为管理员,请在 config.json 中将 <code>|DISPLAYNAME|</code> 添加到 <code>administratorNames</code>`,
 | 
				
			||||||
    cheats_server: `服务器`,
 | 
					    cheats_server: `服务器`,
 | 
				
			||||||
    cheats_skipTutorial: `跳过教程`,
 | 
					    cheats_skipTutorial: `跳过教程`,
 | 
				
			||||||
    cheats_skipAllDialogue: `跳过所有对话`,
 | 
					    cheats_skipAllDialogue: `跳过所有对话`,
 | 
				
			||||||
    cheats_unlockAllScans: `解锁所有扫描`,
 | 
					    cheats_unlockAllScans: `解锁所有扫描`,
 | 
				
			||||||
    cheats_unlockSuccRelog: `[UNTRANSLATED] Success. Please that you'll need to relog for the client to refresh this.`,
 | 
					 | 
				
			||||||
    cheats_unlockAllMissions: `解锁所有星图`,
 | 
					    cheats_unlockAllMissions: `解锁所有星图`,
 | 
				
			||||||
    cheats_unlockAllMissions_ok: `操作成功.请注意,您需要进入道场/中继站或重新登录以刷新星图数据.`,
 | 
					    cheats_unlockAllMissions_ok: `操作成功.请注意,您需要进入道场/中继站或重新登录以刷新星图数据.`,
 | 
				
			||||||
    cheats_infiniteCredits: `无限现金`,
 | 
					    cheats_infiniteCredits: `无限现金`,
 | 
				
			||||||
@ -213,7 +212,7 @@ dict = {
 | 
				
			|||||||
    cheats_baroFullyStocked: `虚空商人贩卖所有商品`,
 | 
					    cheats_baroFullyStocked: `虚空商人贩卖所有商品`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `集团任务可重复完成`,
 | 
					    cheats_syndicateMissionsRepeatable: `集团任务可重复完成`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `解锁利润收割者圆蛛所有阶段`,
 | 
					    cheats_unlockAllProfitTakerStages: `解锁利润收割者圆蛛所有阶段`,
 | 
				
			||||||
    cheats_unlockSuccInventory: `[UNTRANSLATED] Success. Please note that you'll need to resync your inventory, e.g. using the bootstrapper's /sync command, visiting a dojo/relay, or relogging..`,
 | 
					    cheats_unlockSucc: `[UNTRANSLATED] Successfully unlocked.`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`,
 | 
					    cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`,
 | 
				
			||||||
    cheats_instantResourceExtractorDrones: `资源无人机即时完成`,
 | 
					    cheats_instantResourceExtractorDrones: `资源无人机即时完成`,
 | 
				
			||||||
    cheats_noResourceExtractorDronesDamage: `资源无人机不会损毁`,
 | 
					    cheats_noResourceExtractorDronesDamage: `资源无人机不会损毁`,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user