chore(webui): improving inconsistent, long string #2344
							
								
								
									
										18
									
								
								.eslintrc
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								.eslintrc
									
									
									
									
									
								
							@ -11,17 +11,17 @@
 | 
				
			|||||||
        "node": true
 | 
					        "node": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "rules": {
 | 
					    "rules": {
 | 
				
			||||||
        "@typescript-eslint/explicit-function-return-type": "warn",
 | 
					        "@typescript-eslint/explicit-function-return-type": "error",
 | 
				
			||||||
        "@typescript-eslint/restrict-template-expressions": "warn",
 | 
					        "@typescript-eslint/restrict-template-expressions": "error",
 | 
				
			||||||
        "@typescript-eslint/restrict-plus-operands": "warn",
 | 
					        "@typescript-eslint/restrict-plus-operands": "error",
 | 
				
			||||||
        "@typescript-eslint/no-unsafe-member-access": "warn",
 | 
					        "@typescript-eslint/no-unsafe-member-access": "error",
 | 
				
			||||||
        "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "caughtErrors": "none" }],
 | 
					        "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "caughtErrors": "none" }],
 | 
				
			||||||
        "@typescript-eslint/no-unsafe-argument": "error",
 | 
					        "@typescript-eslint/no-unsafe-argument": "error",
 | 
				
			||||||
        "@typescript-eslint/no-unsafe-call": "warn",
 | 
					        "@typescript-eslint/no-unsafe-call": "error",
 | 
				
			||||||
        "@typescript-eslint/no-unsafe-assignment": "warn",
 | 
					        "@typescript-eslint/no-unsafe-assignment": "error",
 | 
				
			||||||
        "@typescript-eslint/no-explicit-any": "warn",
 | 
					        "@typescript-eslint/no-explicit-any": "error",
 | 
				
			||||||
        "no-loss-of-precision": "warn",
 | 
					        "no-loss-of-precision": "error",
 | 
				
			||||||
        "@typescript-eslint/no-unnecessary-condition": "warn",
 | 
					        "@typescript-eslint/no-unnecessary-condition": "error",
 | 
				
			||||||
        "@typescript-eslint/no-base-to-string": "off",
 | 
					        "@typescript-eslint/no-base-to-string": "off",
 | 
				
			||||||
        "no-case-declarations": "error",
 | 
					        "no-case-declarations": "error",
 | 
				
			||||||
        "prettier/prettier": "error",
 | 
					        "prettier/prettier": "error",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							@ -8,8 +8,7 @@
 | 
				
			|||||||
      "type": "node",
 | 
					      "type": "node",
 | 
				
			||||||
      "request": "launch",
 | 
					      "request": "launch",
 | 
				
			||||||
      "name": "Debug and Watch",
 | 
					      "name": "Debug and Watch",
 | 
				
			||||||
      "runtimeArgs": ["-r", "tsconfig-paths/register", "-r", "ts-node/register", "--watch-path", "src"],
 | 
					      "args": ["${workspaceFolder}/scripts/dev.js"],
 | 
				
			||||||
      "args": ["${workspaceFolder}/src/index.ts"],
 | 
					 | 
				
			||||||
      "console": "integratedTerminal"
 | 
					      "console": "integratedTerminal"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
				
			|||||||
@ -34,4 +34,5 @@ SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [confi
 | 
				
			|||||||
  - `RadioLegion2Syndicate` for The Emissary
 | 
					  - `RadioLegion2Syndicate` for The Emissary
 | 
				
			||||||
  - `RadioLegionIntermissionSyndicate` for Intermission I
 | 
					  - `RadioLegionIntermissionSyndicate` for Intermission I
 | 
				
			||||||
  - `RadioLegionSyndicate` for The Wolf of Saturn Six
 | 
					  - `RadioLegionSyndicate` for The Wolf of Saturn Six
 | 
				
			||||||
- `worldState.circuitGameModes` can be provided with an array of valid game modes (`Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, `Alchemy`)
 | 
					- `allTheFissures` can be set to `normal` or `hard` to enable all fissures either in normal or steel path, respectively.
 | 
				
			||||||
 | 
					- `worldState.circuitGameModes` can be set to an array of game modes which will override the otherwise-random pattern in The Circuit. Valid element values are `Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, and `Alchemy`.
 | 
				
			||||||
 | 
				
			|||||||
@ -42,6 +42,8 @@
 | 
				
			|||||||
  "noDeathMarks": false,
 | 
					  "noDeathMarks": false,
 | 
				
			||||||
  "noKimCooldowns": false,
 | 
					  "noKimCooldowns": false,
 | 
				
			||||||
  "fullyStockedVendors": false,
 | 
					  "fullyStockedVendors": false,
 | 
				
			||||||
 | 
					  "baroAlwaysAvailable": false,
 | 
				
			||||||
 | 
					  "baroFullyStocked": false,
 | 
				
			||||||
  "syndicateMissionsRepeatable": false,
 | 
					  "syndicateMissionsRepeatable": false,
 | 
				
			||||||
  "unlockAllProfitTakerStages": false,
 | 
					  "unlockAllProfitTakerStages": false,
 | 
				
			||||||
  "instantFinishRivenChallenge": false,
 | 
					  "instantFinishRivenChallenge": false,
 | 
				
			||||||
@ -56,6 +58,7 @@
 | 
				
			|||||||
  "fastClanAscension": false,
 | 
					  "fastClanAscension": false,
 | 
				
			||||||
  "missionsCanGiveAllRelics": false,
 | 
					  "missionsCanGiveAllRelics": false,
 | 
				
			||||||
  "unlockAllSimarisResearchEntries": false,
 | 
					  "unlockAllSimarisResearchEntries": false,
 | 
				
			||||||
 | 
					  "disableDailyTribute": false,
 | 
				
			||||||
  "spoofMasteryRank": -1,
 | 
					  "spoofMasteryRank": -1,
 | 
				
			||||||
  "nightwaveStandingMultiplier": 1,
 | 
					  "nightwaveStandingMultiplier": 1,
 | 
				
			||||||
  "unfaithfulBugFixes": {
 | 
					  "unfaithfulBugFixes": {
 | 
				
			||||||
@ -67,11 +70,14 @@
 | 
				
			|||||||
    "affinityBoost": false,
 | 
					    "affinityBoost": false,
 | 
				
			||||||
    "resourceBoost": false,
 | 
					    "resourceBoost": false,
 | 
				
			||||||
    "starDays": true,
 | 
					    "starDays": true,
 | 
				
			||||||
 | 
					    "galleonOfGhouls": 0,
 | 
				
			||||||
    "eidolonOverride": "",
 | 
					    "eidolonOverride": "",
 | 
				
			||||||
    "vallisOverride": "",
 | 
					    "vallisOverride": "",
 | 
				
			||||||
    "duviriOverride": "",
 | 
					    "duviriOverride": "",
 | 
				
			||||||
    "nightwaveOverride": "",
 | 
					    "nightwaveOverride": "",
 | 
				
			||||||
    "circuitGameModes": null
 | 
					    "allTheFissures": "",
 | 
				
			||||||
 | 
					    "circuitGameModes": null,
 | 
				
			||||||
 | 
					    "darvoStockMultiplier": 1
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dev": {
 | 
					  "dev": {
 | 
				
			||||||
    "keepVendorsExpired": false
 | 
					    "keepVendorsExpired": false
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										84
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										84
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -13,6 +13,8 @@
 | 
				
			|||||||
        "@types/morgan": "^1.9.9",
 | 
					        "@types/morgan": "^1.9.9",
 | 
				
			||||||
        "@types/websocket": "^1.0.10",
 | 
					        "@types/websocket": "^1.0.10",
 | 
				
			||||||
        "@types/ws": "^8.18.1",
 | 
					        "@types/ws": "^8.18.1",
 | 
				
			||||||
 | 
					        "@typescript/native-preview": "^7.0.0-dev.20250625.1",
 | 
				
			||||||
 | 
					        "chokidar": "^4.0.3",
 | 
				
			||||||
        "crc-32": "^1.2.2",
 | 
					        "crc-32": "^1.2.2",
 | 
				
			||||||
        "express": "^5",
 | 
					        "express": "^5",
 | 
				
			||||||
        "json-with-bigint": "^3.4.4",
 | 
					        "json-with-bigint": "^3.4.4",
 | 
				
			||||||
@ -21,7 +23,7 @@
 | 
				
			|||||||
        "ncp": "^2.0.0",
 | 
					        "ncp": "^2.0.0",
 | 
				
			||||||
        "typescript": "^5.5",
 | 
					        "typescript": "^5.5",
 | 
				
			||||||
        "undici": "^7.10.0",
 | 
					        "undici": "^7.10.0",
 | 
				
			||||||
        "warframe-public-export-plus": "^0.5.68",
 | 
					        "warframe-public-export-plus": "^0.5.74",
 | 
				
			||||||
        "warframe-riven-info": "^0.1.2",
 | 
					        "warframe-riven-info": "^0.1.2",
 | 
				
			||||||
        "winston": "^3.17.0",
 | 
					        "winston": "^3.17.0",
 | 
				
			||||||
        "winston-daily-rotate-file": "^5.0.0",
 | 
					        "winston-daily-rotate-file": "^5.0.0",
 | 
				
			||||||
@ -30,8 +32,6 @@
 | 
				
			|||||||
      "devDependencies": {
 | 
					      "devDependencies": {
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": "^8.28.0",
 | 
					        "@typescript-eslint/eslint-plugin": "^8.28.0",
 | 
				
			||||||
        "@typescript-eslint/parser": "^8.28.0",
 | 
					        "@typescript-eslint/parser": "^8.28.0",
 | 
				
			||||||
        "@typescript/native-preview": "^7.0.0-dev.20250523.1",
 | 
					 | 
				
			||||||
        "chokidar": "^4.0.3",
 | 
					 | 
				
			||||||
        "eslint": "^8",
 | 
					        "eslint": "^8",
 | 
				
			||||||
        "eslint-plugin-prettier": "^5.2.5",
 | 
					        "eslint-plugin-prettier": "^5.2.5",
 | 
				
			||||||
        "prettier": "^3.5.3",
 | 
					        "prettier": "^3.5.3",
 | 
				
			||||||
@ -605,10 +605,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@typescript/native-preview": {
 | 
					    "node_modules/@typescript/native-preview": {
 | 
				
			||||||
      "version": "7.0.0-dev.20250523.1",
 | 
					      "version": "7.0.0-dev.20250625.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20250523.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20250625.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-CgdgP/gmyaMThY7Fho19nDaTVryn9QV/zD/6w1KfDCn3M4Rq4WvkSc7Ob1ohc4V1XjCSIzg6Ul+HbLEc7xvV4Q==",
 | 
					      "integrity": "sha512-7781zmsKURCHknc37H4U4la4kZduyxmmUshZLBzNhPHhV5DKo++K8MF69kxhRG3/vS4HBhozf0YI0mZMIbkSDA==",
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "Apache-2.0",
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
      "bin": {
 | 
					      "bin": {
 | 
				
			||||||
        "tsgo": "bin/tsgo.js"
 | 
					        "tsgo": "bin/tsgo.js"
 | 
				
			||||||
@ -617,23 +616,22 @@
 | 
				
			|||||||
        "node": ">=20.6.0"
 | 
					        "node": ">=20.6.0"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "optionalDependencies": {
 | 
					      "optionalDependencies": {
 | 
				
			||||||
        "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20250523.1",
 | 
					        "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20250625.1",
 | 
				
			||||||
        "@typescript/native-preview-darwin-x64": "7.0.0-dev.20250523.1",
 | 
					        "@typescript/native-preview-darwin-x64": "7.0.0-dev.20250625.1",
 | 
				
			||||||
        "@typescript/native-preview-linux-arm": "7.0.0-dev.20250523.1",
 | 
					        "@typescript/native-preview-linux-arm": "7.0.0-dev.20250625.1",
 | 
				
			||||||
        "@typescript/native-preview-linux-arm64": "7.0.0-dev.20250523.1",
 | 
					        "@typescript/native-preview-linux-arm64": "7.0.0-dev.20250625.1",
 | 
				
			||||||
        "@typescript/native-preview-linux-x64": "7.0.0-dev.20250523.1",
 | 
					        "@typescript/native-preview-linux-x64": "7.0.0-dev.20250625.1",
 | 
				
			||||||
        "@typescript/native-preview-win32-arm64": "7.0.0-dev.20250523.1",
 | 
					        "@typescript/native-preview-win32-arm64": "7.0.0-dev.20250625.1",
 | 
				
			||||||
        "@typescript/native-preview-win32-x64": "7.0.0-dev.20250523.1"
 | 
					        "@typescript/native-preview-win32-x64": "7.0.0-dev.20250625.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@typescript/native-preview-darwin-arm64": {
 | 
					    "node_modules/@typescript/native-preview-darwin-arm64": {
 | 
				
			||||||
      "version": "7.0.0-dev.20250523.1",
 | 
					      "version": "7.0.0-dev.20250625.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20250523.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20250625.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-oWJMPD+lfH9/dvHhPSZdTv43lfyZGrn7crytefhkiQPSwP0MIUCpnDkofGP/ML1nv0xx0pwWhH+Ein88NW3LuA==",
 | 
					      "integrity": "sha512-JcLCql0O6+0iHIMllvax02kqpNtY1RUckGKomuO5kSbrOo9PsR+6r5MEcspfj47gwOl7AS0vrGhBCFFogF+KGw==",
 | 
				
			||||||
      "cpu": [
 | 
					      "cpu": [
 | 
				
			||||||
        "arm64"
 | 
					        "arm64"
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "Apache-2.0",
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
      "optional": true,
 | 
					      "optional": true,
 | 
				
			||||||
      "os": [
 | 
					      "os": [
 | 
				
			||||||
@ -644,13 +642,12 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@typescript/native-preview-darwin-x64": {
 | 
					    "node_modules/@typescript/native-preview-darwin-x64": {
 | 
				
			||||||
      "version": "7.0.0-dev.20250523.1",
 | 
					      "version": "7.0.0-dev.20250625.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20250523.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20250625.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-Yk8bJEsYsRKgRqYlwPvh7DPdgBMC/oPN60X0LWeuMLci65+4kyqF8Cv6K/W3ABc005cB4tYn4iR+9T6zipvrKw==",
 | 
					      "integrity": "sha512-0vCkk3FdS92W625JyzA8Slu/0vgkeu10fRQNfgIbf+E29DKMKnwXW56WhHSdGXAivU44Mewwc589+CbsABq3Sw==",
 | 
				
			||||||
      "cpu": [
 | 
					      "cpu": [
 | 
				
			||||||
        "x64"
 | 
					        "x64"
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "Apache-2.0",
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
      "optional": true,
 | 
					      "optional": true,
 | 
				
			||||||
      "os": [
 | 
					      "os": [
 | 
				
			||||||
@ -661,13 +658,12 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@typescript/native-preview-linux-arm": {
 | 
					    "node_modules/@typescript/native-preview-linux-arm": {
 | 
				
			||||||
      "version": "7.0.0-dev.20250523.1",
 | 
					      "version": "7.0.0-dev.20250625.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20250523.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20250625.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-B+8CRIv6ebL8gzAagnJP8wml3baFV2FtFWuXYl6jlAcLGoQOh/yGdcAueZoJjJKNod4gAOl8OJoTicuC0BVIxw==",
 | 
					      "integrity": "sha512-MumU7p+09ikH/x5IOJRV6DUj6N5/0kSlI4IsAUPtpT2WGkQdDtL2CC523/94YvOfWB1/+9r01636LVCGOJ135g==",
 | 
				
			||||||
      "cpu": [
 | 
					      "cpu": [
 | 
				
			||||||
        "arm"
 | 
					        "arm"
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "Apache-2.0",
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
      "optional": true,
 | 
					      "optional": true,
 | 
				
			||||||
      "os": [
 | 
					      "os": [
 | 
				
			||||||
@ -678,13 +674,12 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@typescript/native-preview-linux-arm64": {
 | 
					    "node_modules/@typescript/native-preview-linux-arm64": {
 | 
				
			||||||
      "version": "7.0.0-dev.20250523.1",
 | 
					      "version": "7.0.0-dev.20250625.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20250523.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20250625.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-IErNI08z9qE6mHaJaT6tM7il8j21ryH3DNVyFP4yz5FTKnkXFj1Kb4NcI41Q8w226LTQgBR8kNErVlbUWr7ywA==",
 | 
					      "integrity": "sha512-IgnoWQSKeoeL7Y7tvlbcDQx0nidK3UWa/bbm1zJv+AfQlAGMrEMygp+ZzocmycUCYOVM0dcIbymjoiI/QRHTng==",
 | 
				
			||||||
      "cpu": [
 | 
					      "cpu": [
 | 
				
			||||||
        "arm64"
 | 
					        "arm64"
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "Apache-2.0",
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
      "optional": true,
 | 
					      "optional": true,
 | 
				
			||||||
      "os": [
 | 
					      "os": [
 | 
				
			||||||
@ -695,13 +690,12 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@typescript/native-preview-linux-x64": {
 | 
					    "node_modules/@typescript/native-preview-linux-x64": {
 | 
				
			||||||
      "version": "7.0.0-dev.20250523.1",
 | 
					      "version": "7.0.0-dev.20250625.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20250523.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20250625.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-TCZtknsLUgPRaEfX9CvBZNgrHhMRZPYYZgF1Aasdv0PONv9mB8w0Xforgxoo4UFjdF5ZzOu2icgc7sKJJeu5vw==",
 | 
					      "integrity": "sha512-6fE8piqPfzPPqmQ37ewTSbm4HW0cNqOEhfLG2F37zJd4525mefhIpWvj2iCkEHWp+BDlF2dYCbB4cY2nmfrNNw==",
 | 
				
			||||||
      "cpu": [
 | 
					      "cpu": [
 | 
				
			||||||
        "x64"
 | 
					        "x64"
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "Apache-2.0",
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
      "optional": true,
 | 
					      "optional": true,
 | 
				
			||||||
      "os": [
 | 
					      "os": [
 | 
				
			||||||
@ -712,13 +706,12 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@typescript/native-preview-win32-arm64": {
 | 
					    "node_modules/@typescript/native-preview-win32-arm64": {
 | 
				
			||||||
      "version": "7.0.0-dev.20250523.1",
 | 
					      "version": "7.0.0-dev.20250625.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20250523.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20250625.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-bulwrkLEkoY4Jqeuvfz24RiVOiZZ7Rr9TblFqZAgZFZOnyXuhjM1jE8F1hnJFC5AghJe2HdLD3EKfabqlffrIw==",
 | 
					      "integrity": "sha512-ppCkjBAFotPxL8j9Vk5cNSwMreOvAt02AMa5Hko3JQGSVA2TQCIlvTFn+SHSIWzYbzomc9j4j5WOcOR0rmAAHg==",
 | 
				
			||||||
      "cpu": [
 | 
					      "cpu": [
 | 
				
			||||||
        "arm64"
 | 
					        "arm64"
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "Apache-2.0",
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
      "optional": true,
 | 
					      "optional": true,
 | 
				
			||||||
      "os": [
 | 
					      "os": [
 | 
				
			||||||
@ -729,13 +722,12 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@typescript/native-preview-win32-x64": {
 | 
					    "node_modules/@typescript/native-preview-win32-x64": {
 | 
				
			||||||
      "version": "7.0.0-dev.20250523.1",
 | 
					      "version": "7.0.0-dev.20250625.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20250523.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20250625.1.tgz",
 | 
				
			||||||
      "integrity": "sha512-ztzfO0oF/rj8xO5y3SyAcigmgvgczrqobCugEWFqiYumteWZPN2MYWcNYk2k8Y5LAgg1fN1xHIg8RRSPoo6XUg==",
 | 
					      "integrity": "sha512-BsnJqso5MKAW4Y7fPmcamJ+EIrWOTqwLjeZP74NNFvTqCsA4RkITCw4NpLwD0lzrv9VsQcQ+bNwB8DrT+oDqoQ==",
 | 
				
			||||||
      "cpu": [
 | 
					      "cpu": [
 | 
				
			||||||
        "x64"
 | 
					        "x64"
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "Apache-2.0",
 | 
					      "license": "Apache-2.0",
 | 
				
			||||||
      "optional": true,
 | 
					      "optional": true,
 | 
				
			||||||
      "os": [
 | 
					      "os": [
 | 
				
			||||||
@ -1005,7 +997,6 @@
 | 
				
			|||||||
      "version": "4.0.3",
 | 
					      "version": "4.0.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
 | 
				
			||||||
      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
 | 
					      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "MIT",
 | 
					      "license": "MIT",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "readdirp": "^4.0.1"
 | 
					        "readdirp": "^4.0.1"
 | 
				
			||||||
@ -2811,7 +2802,6 @@
 | 
				
			|||||||
      "version": "4.1.2",
 | 
					      "version": "4.1.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
 | 
				
			||||||
      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
 | 
					      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "license": "MIT",
 | 
					      "license": "MIT",
 | 
				
			||||||
      "engines": {
 | 
					      "engines": {
 | 
				
			||||||
        "node": ">= 14.18.0"
 | 
					        "node": ">= 14.18.0"
 | 
				
			||||||
@ -3396,9 +3386,9 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/warframe-public-export-plus": {
 | 
					    "node_modules/warframe-public-export-plus": {
 | 
				
			||||||
      "version": "0.5.68",
 | 
					      "version": "0.5.74",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.68.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.74.tgz",
 | 
				
			||||||
      "integrity": "sha512-KMmwCVeQ4k+EN73UZqxnM+qQdPsST8geWoJCP7US5LT6JcRxa8ptmqYXwCzaLtckBLZyVbamsxKZAxPPJckxsA=="
 | 
					      "integrity": "sha512-pA7dxA0lKn9w/2Sc97oxnn+CEzL1SrT9XriNLTDF4Xp+2SBEpGcfbqbdR9ljPQJopIbrc9Zy02R+uBQVomcwyA=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/warframe-riven-info": {
 | 
					    "node_modules/warframe-riven-info": {
 | 
				
			||||||
      "version": "0.1.2",
 | 
					      "version": "0.1.2",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
									
									
									
									
								
							@ -5,8 +5,10 @@
 | 
				
			|||||||
  "main": "index.ts",
 | 
					  "main": "index.ts",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "start": "node --enable-source-maps --import ./build/src/pathman.js build/src/index.js",
 | 
					    "start": "node --enable-source-maps --import ./build/src/pathman.js build/src/index.js",
 | 
				
			||||||
    "build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
 | 
					    "build": "tsgo --sourceMap && ncp static/webui build/static/webui",
 | 
				
			||||||
    "build:dev": "tsc --incremental --sourceMap",
 | 
					    "build:tsc": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
 | 
				
			||||||
 | 
					    "build:dev": "tsgo --sourceMap",
 | 
				
			||||||
 | 
					    "build:dev:tsc": "tsc --incremental --sourceMap",
 | 
				
			||||||
    "build-and-start": "npm run build && npm run start",
 | 
					    "build-and-start": "npm run build && npm run start",
 | 
				
			||||||
    "build-and-start:bun": "npm run verify && npm run bun-run",
 | 
					    "build-and-start:bun": "npm run verify && npm run bun-run",
 | 
				
			||||||
    "dev": "node scripts/dev.js",
 | 
					    "dev": "node scripts/dev.js",
 | 
				
			||||||
@ -25,6 +27,8 @@
 | 
				
			|||||||
    "@types/morgan": "^1.9.9",
 | 
					    "@types/morgan": "^1.9.9",
 | 
				
			||||||
    "@types/websocket": "^1.0.10",
 | 
					    "@types/websocket": "^1.0.10",
 | 
				
			||||||
    "@types/ws": "^8.18.1",
 | 
					    "@types/ws": "^8.18.1",
 | 
				
			||||||
 | 
					    "@typescript/native-preview": "^7.0.0-dev.20250625.1",
 | 
				
			||||||
 | 
					    "chokidar": "^4.0.3",
 | 
				
			||||||
    "crc-32": "^1.2.2",
 | 
					    "crc-32": "^1.2.2",
 | 
				
			||||||
    "express": "^5",
 | 
					    "express": "^5",
 | 
				
			||||||
    "json-with-bigint": "^3.4.4",
 | 
					    "json-with-bigint": "^3.4.4",
 | 
				
			||||||
@ -33,7 +37,7 @@
 | 
				
			|||||||
    "ncp": "^2.0.0",
 | 
					    "ncp": "^2.0.0",
 | 
				
			||||||
    "typescript": "^5.5",
 | 
					    "typescript": "^5.5",
 | 
				
			||||||
    "undici": "^7.10.0",
 | 
					    "undici": "^7.10.0",
 | 
				
			||||||
    "warframe-public-export-plus": "^0.5.68",
 | 
					    "warframe-public-export-plus": "^0.5.74",
 | 
				
			||||||
    "warframe-riven-info": "^0.1.2",
 | 
					    "warframe-riven-info": "^0.1.2",
 | 
				
			||||||
    "winston": "^3.17.0",
 | 
					    "winston": "^3.17.0",
 | 
				
			||||||
    "winston-daily-rotate-file": "^5.0.0",
 | 
					    "winston-daily-rotate-file": "^5.0.0",
 | 
				
			||||||
@ -42,8 +46,6 @@
 | 
				
			|||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^8.28.0",
 | 
					    "@typescript-eslint/eslint-plugin": "^8.28.0",
 | 
				
			||||||
    "@typescript-eslint/parser": "^8.28.0",
 | 
					    "@typescript-eslint/parser": "^8.28.0",
 | 
				
			||||||
    "@typescript/native-preview": "^7.0.0-dev.20250523.1",
 | 
					 | 
				
			||||||
    "chokidar": "^4.0.3",
 | 
					 | 
				
			||||||
    "eslint": "^8",
 | 
					    "eslint": "^8",
 | 
				
			||||||
    "eslint-plugin-prettier": "^5.2.5",
 | 
					    "eslint-plugin-prettier": "^5.2.5",
 | 
				
			||||||
    "prettier": "^3.5.3",
 | 
					    "prettier": "^3.5.3",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,12 @@
 | 
				
			|||||||
 | 
					import { getAccountForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const checkDailyMissionBonusController: RequestHandler = (_req, res) => {
 | 
					export const checkDailyMissionBonusController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const data = Buffer.from([
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
        0x44, 0x61, 0x69, 0x6c, 0x79, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x6e, 0x75, 0x73, 0x3a,
 | 
					    const today = Math.trunc(Date.now() / 86400000) * 86400;
 | 
				
			||||||
        0x31, 0x2d, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x50, 0x56, 0x50, 0x57, 0x69, 0x6e, 0x42, 0x6f, 0x6e, 0x75, 0x73,
 | 
					    if (account.DailyFirstWinDate != today) {
 | 
				
			||||||
        0x3a, 0x31, 0x0a
 | 
					        res.send("DailyMissionBonus:1-DailyPVPWinBonus:1\n");
 | 
				
			||||||
    ]);
 | 
					    } else {
 | 
				
			||||||
    res.writeHead(200, {
 | 
					        res.send("DailyMissionBonus:0-DailyPVPWinBonus:1\n");
 | 
				
			||||||
        "Content-Type": "text/html",
 | 
					    }
 | 
				
			||||||
        "Content-Length": data.length
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    res.end(data);
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
export { checkDailyMissionBonusController };
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,8 @@ import {
 | 
				
			|||||||
    addItem,
 | 
					    addItem,
 | 
				
			||||||
    addRecipes,
 | 
					    addRecipes,
 | 
				
			||||||
    occupySlot,
 | 
					    occupySlot,
 | 
				
			||||||
    combineInventoryChanges
 | 
					    combineInventoryChanges,
 | 
				
			||||||
 | 
					    addKubrowPetPrint
 | 
				
			||||||
} from "@/src/services/inventoryService";
 | 
					} from "@/src/services/inventoryService";
 | 
				
			||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
					import { IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
				
			||||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
					import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
				
			||||||
@ -119,6 +120,9 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            pet.Details!.Status = canSetActive ? Status.StatusAvailable : Status.StatusStasis;
 | 
					            pet.Details!.Status = canSetActive ? Status.StatusAvailable : Status.StatusStasis;
 | 
				
			||||||
 | 
					        } else if (recipe.secretIngredientAction == "SIA_DISTILL_PRINT") {
 | 
				
			||||||
 | 
					            const pet = inventory.KubrowPets.id(pendingRecipe.KubrowPet!)!;
 | 
				
			||||||
 | 
					            addKubrowPetPrint(inventory, pet, InventoryChanges);
 | 
				
			||||||
        } else if (recipe.secretIngredientAction != "SIA_UNBRAND") {
 | 
					        } else if (recipe.secretIngredientAction != "SIA_UNBRAND") {
 | 
				
			||||||
            InventoryChanges = {
 | 
					            InventoryChanges = {
 | 
				
			||||||
                ...InventoryChanges,
 | 
					                ...InventoryChanges,
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
 | 
					import { combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
 | 
					import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
				
			||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					import { ExportChallenges } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const claimJunctionChallengeRewardController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
 | 
					    const inventory = await getInventory(accountId);
 | 
				
			||||||
 | 
					    const data = getJSONfromString<IClaimJunctionChallengeRewardRequest>(String(req.body));
 | 
				
			||||||
 | 
					    const challengeProgress = inventory.ChallengeProgress.find(x => x.Name == data.Challenge)!;
 | 
				
			||||||
 | 
					    if (challengeProgress.ReceivedJunctionReward) {
 | 
				
			||||||
 | 
					        throw new Error(`attempt to double-claim junction reward`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    challengeProgress.ReceivedJunctionReward = true;
 | 
				
			||||||
 | 
					    inventory.ClaimedJunctionChallengeRewards ??= [];
 | 
				
			||||||
 | 
					    inventory.ClaimedJunctionChallengeRewards.push(data.Challenge);
 | 
				
			||||||
 | 
					    const challengeMeta = Object.entries(ExportChallenges).find(arr => arr[0].endsWith("/" + data.Challenge))![1];
 | 
				
			||||||
 | 
					    const inventoryChanges = {};
 | 
				
			||||||
 | 
					    for (const reward of challengeMeta.countedRewards!) {
 | 
				
			||||||
 | 
					        combineInventoryChanges(
 | 
				
			||||||
 | 
					            inventoryChanges,
 | 
				
			||||||
 | 
					            (await handleStoreItemAcquisition(reward.StoreItem, inventory, reward.ItemCount)).InventoryChanges
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await inventory.save();
 | 
				
			||||||
 | 
					    res.json({
 | 
				
			||||||
 | 
					        inventoryChanges: inventoryChanges // Yeah, it's "inventoryChanges" in the response here.
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IClaimJunctionChallengeRewardRequest {
 | 
				
			||||||
 | 
					    Challenge: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,8 +1,10 @@
 | 
				
			|||||||
 | 
					import { DailyDeal } from "@/src/models/worldStateModel";
 | 
				
			||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getDailyDealStockLevelsController: RequestHandler = (req, res) => {
 | 
					export const getDailyDealStockLevelsController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    const dailyDeal = (await DailyDeal.findOne({ StoreItem: req.query.productName }, "AmountSold"))!;
 | 
				
			||||||
    res.json({
 | 
					    res.json({
 | 
				
			||||||
        StoreItem: req.query.productName,
 | 
					        StoreItem: req.query.productName,
 | 
				
			||||||
        AmountSold: 0
 | 
					        AmountSold: dailyDeal.AmountSold
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -9,15 +9,26 @@ import {
 | 
				
			|||||||
    updateCurrency
 | 
					    updateCurrency
 | 
				
			||||||
} from "@/src/services/inventoryService";
 | 
					} from "@/src/services/inventoryService";
 | 
				
			||||||
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
 | 
					import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
 | 
				
			||||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
					import { handleDailyDealPurchase, handleStoreItemAcquisition } from "@/src/services/purchaseService";
 | 
				
			||||||
import { IOid } from "@/src/types/commonTypes";
 | 
					import { IOid } from "@/src/types/commonTypes";
 | 
				
			||||||
import { IInventoryChanges, IPurchaseParams, PurchaseSource } from "@/src/types/purchaseTypes";
 | 
					import { IPurchaseParams, IPurchaseResponse, PurchaseSource } from "@/src/types/purchaseTypes";
 | 
				
			||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import { ExportBundles, ExportFlavour } from "warframe-public-export-plus";
 | 
					import { ExportBundles, ExportFlavour } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const checkPurchaseParams = (params: IPurchaseParams): boolean => {
 | 
				
			||||||
 | 
					    switch (params.Source) {
 | 
				
			||||||
 | 
					        case PurchaseSource.Market:
 | 
				
			||||||
 | 
					            return params.UsePremium;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case PurchaseSource.DailyDeal:
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const giftingController: RequestHandler = async (req, res) => {
 | 
					export const giftingController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const data = getJSONfromString<IGiftingRequest>(String(req.body));
 | 
					    const data = getJSONfromString<IGiftingRequest>(String(req.body));
 | 
				
			||||||
    if (data.PurchaseParams.Source != PurchaseSource.Market || !data.PurchaseParams.UsePremium) {
 | 
					    if (!checkPurchaseParams(data.PurchaseParams)) {
 | 
				
			||||||
        throw new Error(`unexpected purchase params in gifting request: ${String(req.body)}`);
 | 
					        throw new Error(`unexpected purchase params in gifting request: ${String(req.body)}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -58,16 +69,19 @@ export const giftingController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    senderInventory.GiftsRemaining -= 1;
 | 
					    senderInventory.GiftsRemaining -= 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const inventoryChanges: IInventoryChanges = updateCurrency(
 | 
					    const response: IPurchaseResponse = {
 | 
				
			||||||
        senderInventory,
 | 
					        InventoryChanges: {}
 | 
				
			||||||
        data.PurchaseParams.ExpectedPrice,
 | 
					    };
 | 
				
			||||||
        true
 | 
					    if (data.PurchaseParams.Source == PurchaseSource.DailyDeal) {
 | 
				
			||||||
    );
 | 
					        await handleDailyDealPurchase(senderInventory, data.PurchaseParams, response);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        updateCurrency(senderInventory, data.PurchaseParams.ExpectedPrice, true, response.InventoryChanges);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (data.PurchaseParams.StoreItem in ExportBundles) {
 | 
					    if (data.PurchaseParams.StoreItem in ExportBundles) {
 | 
				
			||||||
        const bundle = ExportBundles[data.PurchaseParams.StoreItem];
 | 
					        const bundle = ExportBundles[data.PurchaseParams.StoreItem];
 | 
				
			||||||
        if (bundle.giftingBonus) {
 | 
					        if (bundle.giftingBonus) {
 | 
				
			||||||
            combineInventoryChanges(
 | 
					            combineInventoryChanges(
 | 
				
			||||||
                inventoryChanges,
 | 
					                response.InventoryChanges,
 | 
				
			||||||
                (await handleStoreItemAcquisition(bundle.giftingBonus, senderInventory)).InventoryChanges
 | 
					                (await handleStoreItemAcquisition(bundle.giftingBonus, senderInventory)).InventoryChanges
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -99,9 +113,7 @@ export const giftingController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.json({
 | 
					    res.json(response);
 | 
				
			||||||
        InventoryChanges: inventoryChanges
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IGiftingRequest {
 | 
					interface IGiftingRequest {
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,10 @@ import { getNemesisManifest } from "@/src/helpers/nemesisHelpers";
 | 
				
			|||||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
 | 
					import { getPersonalRooms } from "@/src/services/personalRoomsService";
 | 
				
			||||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
 | 
					import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
 | 
				
			||||||
import { Ship } from "@/src/models/shipModel";
 | 
					import { Ship } from "@/src/models/shipModel";
 | 
				
			||||||
import { toLegacyOid, version_compare } from "@/src/helpers/inventoryHelpers";
 | 
					import { toLegacyOid, toOid, version_compare } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					import { Inbox } from "@/src/models/inboxModel";
 | 
				
			||||||
 | 
					import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
				
			||||||
 | 
					import { DailyDeal } from "@/src/models/worldStateModel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const inventoryController: RequestHandler = async (request, response) => {
 | 
					export const inventoryController: RequestHandler = async (request, response) => {
 | 
				
			||||||
    const account = await getAccountForRequest(request);
 | 
					    const account = await getAccountForRequest(request);
 | 
				
			||||||
@ -36,6 +39,8 @@ export const inventoryController: RequestHandler = async (request, response) =>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Handle daily reset
 | 
					    // Handle daily reset
 | 
				
			||||||
    if (!inventory.NextRefill || Date.now() >= inventory.NextRefill.getTime()) {
 | 
					    if (!inventory.NextRefill || Date.now() >= inventory.NextRefill.getTime()) {
 | 
				
			||||||
 | 
					        const today = Math.trunc(Date.now() / 86400000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (const key of allDailyAffiliationKeys) {
 | 
					        for (const key of allDailyAffiliationKeys) {
 | 
				
			||||||
            inventory[key] = 16000 + inventory.PlayerLevel * 500;
 | 
					            inventory[key] = 16000 + inventory.PlayerLevel * 500;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -46,12 +51,12 @@ export const inventoryController: RequestHandler = async (request, response) =>
 | 
				
			|||||||
        inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
 | 
					        inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (inventory.NextRefill) {
 | 
					        if (inventory.NextRefill) {
 | 
				
			||||||
 | 
					            const lastLoginDay = Math.trunc(inventory.NextRefill.getTime() / 86400000) - 1;
 | 
				
			||||||
 | 
					            const daysPassed = today - lastLoginDay;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (config.noArgonCrystalDecay) {
 | 
					            if (config.noArgonCrystalDecay) {
 | 
				
			||||||
                inventory.FoundToday = undefined;
 | 
					                inventory.FoundToday = undefined;
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                const lastLoginDay = Math.trunc(inventory.NextRefill.getTime() / 86400000) - 1;
 | 
					 | 
				
			||||||
                const today = Math.trunc(Date.now() / 86400000);
 | 
					 | 
				
			||||||
                const daysPassed = today - lastLoginDay;
 | 
					 | 
				
			||||||
                for (let i = 0; i != daysPassed; ++i) {
 | 
					                for (let i = 0; i != daysPassed; ++i) {
 | 
				
			||||||
                    const numArgonCrystals =
 | 
					                    const numArgonCrystals =
 | 
				
			||||||
                        inventory.MiscItems.find(x => x.ItemType == "/Lotus/Types/Items/MiscItems/ArgonCrystal")
 | 
					                        inventory.MiscItems.find(x => x.ItemType == "/Lotus/Types/Items/MiscItems/ArgonCrystal")
 | 
				
			||||||
@ -83,11 +88,29 @@ export const inventoryController: RequestHandler = async (request, response) =>
 | 
				
			|||||||
                    inventory.FoundToday = undefined;
 | 
					                    inventory.FoundToday = undefined;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (inventory.UsedDailyDeals.length != 0) {
 | 
				
			||||||
 | 
					                if (daysPassed == 1) {
 | 
				
			||||||
 | 
					                    const todayAt0Utc = today * 86400000;
 | 
				
			||||||
 | 
					                    const darvoIndex = Math.trunc((todayAt0Utc - 25200000) / (26 * unixTimesInMs.hour));
 | 
				
			||||||
 | 
					                    const darvoStart = darvoIndex * (26 * unixTimesInMs.hour) + 25200000;
 | 
				
			||||||
 | 
					                    const darvoOid =
 | 
				
			||||||
 | 
					                        ((darvoStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "adc51a72f7324d95";
 | 
				
			||||||
 | 
					                    const deal = await DailyDeal.findById(darvoOid);
 | 
				
			||||||
 | 
					                    if (deal) {
 | 
				
			||||||
 | 
					                        inventory.UsedDailyDeals = inventory.UsedDailyDeals.filter(x => x == deal.StoreItem); // keep only the deal that came into this new day with us
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        inventory.UsedDailyDeals = [];
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    inventory.UsedDailyDeals = [];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cleanupInventory(inventory);
 | 
					        cleanupInventory(inventory);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000);
 | 
					        inventory.NextRefill = new Date((today + 1) * 86400000); // tomorrow at 0 UTC
 | 
				
			||||||
        //await inventory.save();
 | 
					        //await inventory.save();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -128,13 +151,21 @@ export const getInventoryResponse = async (
 | 
				
			|||||||
    xpBasedLevelCapDisabled: boolean,
 | 
					    xpBasedLevelCapDisabled: boolean,
 | 
				
			||||||
    buildLabel: string | undefined
 | 
					    buildLabel: string | undefined
 | 
				
			||||||
): Promise<IInventoryClient> => {
 | 
					): Promise<IInventoryClient> => {
 | 
				
			||||||
    const [inventoryWithLoadOutPresets, ships] = await Promise.all([
 | 
					    const [inventoryWithLoadOutPresets, ships, latestMessage] = await Promise.all([
 | 
				
			||||||
        inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>("LoadOutPresets"),
 | 
					        inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>("LoadOutPresets"),
 | 
				
			||||||
        Ship.find({ ShipOwnerId: inventory.accountOwnerId })
 | 
					        Ship.find({ ShipOwnerId: inventory.accountOwnerId }),
 | 
				
			||||||
 | 
					        Inbox.findOne({ ownerId: inventory.accountOwnerId }, "_id").sort({ date: -1 })
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
    const inventoryResponse = inventoryWithLoadOutPresets.toJSON<IInventoryClient>();
 | 
					    const inventoryResponse = inventoryWithLoadOutPresets.toJSON<IInventoryClient>();
 | 
				
			||||||
    inventoryResponse.Ships = ships.map(x => x.toJSON<IShipInventory>());
 | 
					    inventoryResponse.Ships = ships.map(x => x.toJSON<IShipInventory>());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // In case mission inventory update added an inbox message, we need to send the Mailbox part so the client knows to refresh it.
 | 
				
			||||||
 | 
					    if (latestMessage) {
 | 
				
			||||||
 | 
					        inventoryResponse.Mailbox = {
 | 
				
			||||||
 | 
					            LastInboxId: toOid(latestMessage._id)
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (config.infiniteCredits) {
 | 
					    if (config.infiniteCredits) {
 | 
				
			||||||
        inventoryResponse.RegularCredits = 999999999;
 | 
					        inventoryResponse.RegularCredits = 999999999;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -282,9 +313,6 @@ export const getInventoryResponse = async (
 | 
				
			|||||||
        applyCheatsToInfestedFoundry(inventoryResponse.InfestedFoundry);
 | 
					        applyCheatsToInfestedFoundry(inventoryResponse.InfestedFoundry);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Omitting this field so opening the navigation resyncs the inventory which is more desirable for typical usage.
 | 
					 | 
				
			||||||
    inventoryResponse.LastInventorySync = undefined;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Set 2FA enabled so trading post can be used
 | 
					    // Set 2FA enabled so trading post can be used
 | 
				
			||||||
    inventoryResponse.HWIDProtectEnabled = true;
 | 
					    inventoryResponse.HWIDProtectEnabled = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import {
 | 
				
			|||||||
    setAccountGotLoginRewardToday
 | 
					    setAccountGotLoginRewardToday
 | 
				
			||||||
} from "@/src/services/loginRewardService";
 | 
					} from "@/src/services/loginRewardService";
 | 
				
			||||||
import { getInventory } from "@/src/services/inventoryService";
 | 
					import { getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const loginRewardsController: RequestHandler = async (req, res) => {
 | 
					export const loginRewardsController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const account = await getAccountForRequest(req);
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
@ -15,7 +16,7 @@ export const loginRewardsController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    const isMilestoneDay = account.LoginDays == 5 || account.LoginDays % 50 == 0;
 | 
					    const isMilestoneDay = account.LoginDays == 5 || account.LoginDays % 50 == 0;
 | 
				
			||||||
    const nextMilestoneDay = account.LoginDays < 5 ? 5 : (Math.trunc(account.LoginDays / 50) + 1) * 50;
 | 
					    const nextMilestoneDay = account.LoginDays < 5 ? 5 : (Math.trunc(account.LoginDays / 50) + 1) * 50;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (today == account.LastLoginRewardDate) {
 | 
					    if (today == account.LastLoginRewardDate || config.disableDailyTribute) {
 | 
				
			||||||
        res.json({
 | 
					        res.json({
 | 
				
			||||||
            DailyTributeInfo: {
 | 
					            DailyTributeInfo: {
 | 
				
			||||||
                IsMilestoneDay: isMilestoneDay,
 | 
					                IsMilestoneDay: isMilestoneDay,
 | 
				
			||||||
 | 
				
			|||||||
@ -88,7 +88,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
 | 
				
			|||||||
        AffiliationMods,
 | 
					        AffiliationMods,
 | 
				
			||||||
        SyndicateXPItemReward,
 | 
					        SyndicateXPItemReward,
 | 
				
			||||||
        ConquestCompletedMissionsCount
 | 
					        ConquestCompletedMissionsCount
 | 
				
			||||||
    } = await addMissionRewards(inventory, missionReport, firstCompletion);
 | 
					    } = await addMissionRewards(account, inventory, missionReport, firstCompletion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (missionReport.EndOfMatchUpload) {
 | 
					    if (missionReport.EndOfMatchUpload) {
 | 
				
			||||||
        inventory.RewardSeed = generateRewardSeed();
 | 
					        inventory.RewardSeed = generateRewardSeed();
 | 
				
			||||||
 | 
				
			|||||||
@ -57,7 +57,11 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
 | 
				
			|||||||
                component.DecoCapacity -= meta.capacityCost;
 | 
					                component.DecoCapacity -= meta.capacityCost;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            const itemType = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type)![0];
 | 
					            const [itemType, meta] = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type)!;
 | 
				
			||||||
 | 
					            if (!itemType || meta.dojoCapacityCost === undefined) {
 | 
				
			||||||
 | 
					                throw new Error(`unknown deco type: ${deco.Type}`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            component.DecoCapacity -= meta.dojoCapacityCost;
 | 
				
			||||||
            if (deco.Sockets !== undefined) {
 | 
					            if (deco.Sockets !== undefined) {
 | 
				
			||||||
                guild.VaultFusionTreasures!.find(x => x.ItemType == itemType && x.Sockets == deco.Sockets)!.ItemCount -=
 | 
					                guild.VaultFusionTreasures!.find(x => x.ItemType == itemType && x.Sockets == deco.Sockets)!.ItemCount -=
 | 
				
			||||||
                    1;
 | 
					                    1;
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@ export const purchaseController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    const inventory = await getInventory(accountId);
 | 
					    const inventory = await getInventory(accountId);
 | 
				
			||||||
    const response = await handlePurchase(purchaseRequest, inventory);
 | 
					    const response = await handlePurchase(purchaseRequest, inventory);
 | 
				
			||||||
    await inventory.save();
 | 
					    await inventory.save();
 | 
				
			||||||
 | 
					    //console.log(JSON.stringify(response, null, 2));
 | 
				
			||||||
    res.json(response);
 | 
					    res.json(response);
 | 
				
			||||||
    sendWsBroadcastTo(accountId, { update_inventory: true });
 | 
					    sendWsBroadcastTo(accountId, { update_inventory: true });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -45,9 +45,9 @@ export const startRecipeController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    for (let i = 0; i != recipe.ingredients.length; ++i) {
 | 
					    for (let i = 0; i != recipe.ingredients.length; ++i) {
 | 
				
			||||||
        if (startRecipeRequest.Ids[i] && startRecipeRequest.Ids[i][0] != "/") {
 | 
					        if (startRecipeRequest.Ids[i] && startRecipeRequest.Ids[i][0] != "/") {
 | 
				
			||||||
            if (recipe.ingredients[i].ItemType == "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem") {
 | 
					            if (recipe.ingredients[i].ItemType == "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem") {
 | 
				
			||||||
                const index = inventory.KubrowPetEggs!.findIndex(x => x._id.equals(startRecipeRequest.Ids[i]));
 | 
					                const index = inventory.KubrowPetEggs.findIndex(x => x._id.equals(startRecipeRequest.Ids[i]));
 | 
				
			||||||
                if (index != -1) {
 | 
					                if (index != -1) {
 | 
				
			||||||
                    inventory.KubrowPetEggs!.splice(index, 1);
 | 
					                    inventory.KubrowPetEggs.splice(index, 1);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory;
 | 
					                const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory;
 | 
				
			||||||
@ -72,6 +72,10 @@ export const startRecipeController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
    if (recipe.secretIngredientAction == "SIA_CREATE_KUBROW") {
 | 
					    if (recipe.secretIngredientAction == "SIA_CREATE_KUBROW") {
 | 
				
			||||||
        inventoryChanges = addKubrowPet(inventory, getRandomElement(recipe.secretIngredients!)!.ItemType);
 | 
					        inventoryChanges = addKubrowPet(inventory, getRandomElement(recipe.secretIngredients!)!.ItemType);
 | 
				
			||||||
        pr.KubrowPet = new Types.ObjectId(fromOid(inventoryChanges.KubrowPets![0].ItemId));
 | 
					        pr.KubrowPet = new Types.ObjectId(fromOid(inventoryChanges.KubrowPets![0].ItemId));
 | 
				
			||||||
 | 
					    } else if (recipe.secretIngredientAction == "SIA_DISTILL_PRINT") {
 | 
				
			||||||
 | 
					        pr.KubrowPet = new Types.ObjectId(startRecipeRequest.Ids[recipe.ingredients.length]);
 | 
				
			||||||
 | 
					        const pet = inventory.KubrowPets.id(pr.KubrowPet)!;
 | 
				
			||||||
 | 
					        pet.Details!.PrintsRemaining -= 1;
 | 
				
			||||||
    } else if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
 | 
					    } else if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
 | 
				
			||||||
        const spectreLoadout: ISpectreLoadout = {
 | 
					        const spectreLoadout: ISpectreLoadout = {
 | 
				
			||||||
            ItemType: recipe.resultType,
 | 
					            ItemType: recipe.resultType,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
					import { getJSONfromString } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
import { getAccountForRequest } from "@/src/services/loginService";
 | 
					import { getAccountForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { addChallenges, getInventory } from "@/src/services/inventoryService";
 | 
					import { addCalendarProgress, addChallenges, getInventory } from "@/src/services/inventoryService";
 | 
				
			||||||
import { IChallengeProgress, ISeasonChallenge } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IChallengeProgress, ISeasonChallenge } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { IAffiliationMods } from "@/src/types/purchaseTypes";
 | 
					import { IAffiliationMods } from "@/src/types/purchaseTypes";
 | 
				
			||||||
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
 | 
					import { getEntriesUnsafe } from "@/src/utils/ts-utils";
 | 
				
			||||||
@ -25,13 +25,17 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    for (const [key, value] of getEntriesUnsafe(challenges)) {
 | 
					    for (const [key, value] of getEntriesUnsafe(challenges)) {
 | 
				
			||||||
 | 
					        if (value === undefined) {
 | 
				
			||||||
 | 
					            logger.error(`Challenge progress update key ${key} has no value`);
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        switch (key) {
 | 
					        switch (key) {
 | 
				
			||||||
            case "ChallengesFixVersion":
 | 
					            case "ChallengesFixVersion":
 | 
				
			||||||
                inventory.ChallengesFixVersion = value;
 | 
					                inventory.ChallengesFixVersion = value;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case "SeasonChallengeHistory":
 | 
					            case "SeasonChallengeHistory":
 | 
				
			||||||
                value!.forEach(({ challenge, id }) => {
 | 
					                value.forEach(({ challenge, id }) => {
 | 
				
			||||||
                    const itemIndex = inventory.SeasonChallengeHistory.findIndex(i => i.challenge === challenge);
 | 
					                    const itemIndex = inventory.SeasonChallengeHistory.findIndex(i => i.challenge === challenge);
 | 
				
			||||||
                    if (itemIndex !== -1) {
 | 
					                    if (itemIndex !== -1) {
 | 
				
			||||||
                        inventory.SeasonChallengeHistory[itemIndex].id = id;
 | 
					                        inventory.SeasonChallengeHistory[itemIndex].id = id;
 | 
				
			||||||
@ -41,6 +45,10 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
 | 
				
			|||||||
                });
 | 
					                });
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case "CalendarProgress":
 | 
				
			||||||
 | 
					                addCalendarProgress(inventory, value);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            case "ChallengeProgress":
 | 
					            case "ChallengeProgress":
 | 
				
			||||||
            case "SeasonChallengeCompletions":
 | 
					            case "SeasonChallengeCompletions":
 | 
				
			||||||
            case "ChallengePTS":
 | 
					            case "ChallengePTS":
 | 
				
			||||||
@ -63,5 +71,6 @@ interface IUpdateChallengeProgressRequest {
 | 
				
			|||||||
    ChallengeProgress?: IChallengeProgress[];
 | 
					    ChallengeProgress?: IChallengeProgress[];
 | 
				
			||||||
    SeasonChallengeHistory?: ISeasonChallenge[];
 | 
					    SeasonChallengeHistory?: ISeasonChallenge[];
 | 
				
			||||||
    SeasonChallengeCompletions?: ISeasonChallenge[];
 | 
					    SeasonChallengeCompletions?: ISeasonChallenge[];
 | 
				
			||||||
 | 
					    CalendarProgress?: { challenge: string }[];
 | 
				
			||||||
    crossPlaySetting?: string;
 | 
					    crossPlaySetting?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					import { getAccountIdForRequest } from "@/src/services/loginService";
 | 
				
			||||||
 | 
					import { getInventory, addRecipes } from "@/src/services/inventoryService";
 | 
				
			||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					import { ExportRecipes } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const addMissingHelminthBlueprintsController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    const accountId = await getAccountIdForRequest(req);
 | 
				
			||||||
 | 
					    const inventory = await getInventory(accountId, "Recipes");
 | 
				
			||||||
 | 
					    const allHelminthRecipes = Object.keys(ExportRecipes).filter(
 | 
				
			||||||
 | 
					        key => ExportRecipes[key].secretIngredientAction === "SIA_WARFRAME_ABILITY"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    const inventoryHelminthRecipes = inventory.Recipes.filter(recipe =>
 | 
				
			||||||
 | 
					        recipe.ItemType.startsWith("/Lotus/Types/Recipes/AbilityOverrides/")
 | 
				
			||||||
 | 
					    ).map(recipe => recipe.ItemType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const missingHelminthRecipes = allHelminthRecipes
 | 
				
			||||||
 | 
					        .filter(key => !inventoryHelminthRecipes.includes(key))
 | 
				
			||||||
 | 
					        .map(ItemType => ({ ItemType, ItemCount: 1 }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addRecipes(inventory, missingHelminthRecipes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await inventory.save();
 | 
				
			||||||
 | 
					    res.end();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -12,18 +12,24 @@ export const completeAllMissionsController: RequestHandler = async (req, res) =>
 | 
				
			|||||||
    const inventory = await getInventory(accountId);
 | 
					    const inventory = await getInventory(accountId);
 | 
				
			||||||
    const MissionRewards: IMissionReward[] = [];
 | 
					    const MissionRewards: IMissionReward[] = [];
 | 
				
			||||||
    for (const [tag, node] of Object.entries(ExportRegions)) {
 | 
					    for (const [tag, node] of Object.entries(ExportRegions)) {
 | 
				
			||||||
        if (!inventory.Missions.find(x => x.Tag == tag)) {
 | 
					        let mission = inventory.Missions.find(x => x.Tag == tag);
 | 
				
			||||||
            inventory.Missions.push({
 | 
					        if (!mission) {
 | 
				
			||||||
                Completes: 1,
 | 
					            mission =
 | 
				
			||||||
                Tier: 1,
 | 
					                inventory.Missions[
 | 
				
			||||||
                Tag: tag
 | 
					                    inventory.Missions.push({
 | 
				
			||||||
            });
 | 
					                        Completes: 0,
 | 
				
			||||||
 | 
					                        Tier: 0,
 | 
				
			||||||
 | 
					                        Tag: tag
 | 
				
			||||||
 | 
					                    }) - 1
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (mission.Completes == 0) {
 | 
				
			||||||
 | 
					            mission.Completes++;
 | 
				
			||||||
            if (node.missionReward) {
 | 
					            if (node.missionReward) {
 | 
				
			||||||
                console.log(node.missionReward);
 | 
					                addFixedLevelRewards(node.missionReward, MissionRewards);
 | 
				
			||||||
                addFixedLevelRewards(node.missionReward, inventory, MissionRewards);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        mission.Tier = 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    for (const reward of MissionRewards) {
 | 
					    for (const reward of MissionRewards) {
 | 
				
			||||||
        await handleStoreItemAcquisition(reward.StoreItem, inventory, reward.ItemCount, undefined, true);
 | 
					        await handleStoreItemAcquisition(reward.StoreItem, inventory, reward.ItemCount, undefined, true);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										44
									
								
								src/controllers/custom/configController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/controllers/custom/configController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
 | 
					import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
 | 
				
			||||||
 | 
					import { saveConfig } from "@/src/services/configWatcherService";
 | 
				
			||||||
 | 
					import { sendWsBroadcastExcept } from "@/src/services/webService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getConfigController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
 | 
					    if (isAdministrator(account)) {
 | 
				
			||||||
 | 
					        const responseData: Record<string, boolean | string | number | null> = {};
 | 
				
			||||||
 | 
					        for (const id of req.body as string[]) {
 | 
				
			||||||
 | 
					            const [obj, idx] = configIdToIndexable(id);
 | 
				
			||||||
 | 
					            responseData[id] = obj[idx] ?? null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        res.json(responseData);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        res.status(401).end();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const setConfigController: RequestHandler = async (req, res) => {
 | 
				
			||||||
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
 | 
					    if (isAdministrator(account)) {
 | 
				
			||||||
 | 
					        for (const [id, value] of Object.entries(req.body as Record<string, boolean | string | number>)) {
 | 
				
			||||||
 | 
					            const [obj, idx] = configIdToIndexable(id);
 | 
				
			||||||
 | 
					            obj[idx] = value;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        sendWsBroadcastExcept(parseInt(String(req.query.wsid)), { config_reloaded: true });
 | 
				
			||||||
 | 
					        await saveConfig();
 | 
				
			||||||
 | 
					        res.end();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        res.status(401).end();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const configIdToIndexable = (id: string): [Record<string, boolean | string | number | undefined>, string] => {
 | 
				
			||||||
 | 
					    let obj = config as unknown as Record<string, never>;
 | 
				
			||||||
 | 
					    const arr = id.split(".");
 | 
				
			||||||
 | 
					    while (arr.length > 1) {
 | 
				
			||||||
 | 
					        obj = obj[arr[0]];
 | 
				
			||||||
 | 
					        arr.splice(0, 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return [obj, arr[0]];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,14 +0,0 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					 | 
				
			||||||
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getConfigDataController: RequestHandler = async (req, res) => {
 | 
					 | 
				
			||||||
    const account = await getAccountForRequest(req);
 | 
					 | 
				
			||||||
    if (isAdministrator(account)) {
 | 
					 | 
				
			||||||
        res.json(config);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        res.status(401).end();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export { getConfigDataController };
 | 
					 | 
				
			||||||
@ -55,6 +55,7 @@ interface ItemLists {
 | 
				
			|||||||
    EvolutionProgress: ListedItem[];
 | 
					    EvolutionProgress: ListedItem[];
 | 
				
			||||||
    mods: ListedItem[];
 | 
					    mods: ListedItem[];
 | 
				
			||||||
    Boosters: ListedItem[];
 | 
					    Boosters: ListedItem[];
 | 
				
			||||||
 | 
					    //circuitGameModes: ListedItem[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const relicQualitySuffixes: Record<TRelicQuality, string> = {
 | 
					const relicQualitySuffixes: Record<TRelicQuality, string> = {
 | 
				
			||||||
@ -64,6 +65,10 @@ const relicQualitySuffixes: Record<TRelicQuality, string> = {
 | 
				
			|||||||
    VPQ_PLATINUM: " [Exceptional]"
 | 
					    VPQ_PLATINUM: " [Exceptional]"
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*const toTitleCase = (str: string): string => {
 | 
				
			||||||
 | 
					    return str.replace(/[^\s-]+/g, word => word.charAt(0).toUpperCase() + word.substr(1).toLowerCase());
 | 
				
			||||||
 | 
					};*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getItemListsController: RequestHandler = (req, response) => {
 | 
					const getItemListsController: RequestHandler = (req, response) => {
 | 
				
			||||||
    const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
 | 
					    const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
 | 
				
			||||||
    const res: ItemLists = {
 | 
					    const res: ItemLists = {
 | 
				
			||||||
@ -87,6 +92,36 @@ const getItemListsController: RequestHandler = (req, response) => {
 | 
				
			|||||||
        EvolutionProgress: [],
 | 
					        EvolutionProgress: [],
 | 
				
			||||||
        mods: [],
 | 
					        mods: [],
 | 
				
			||||||
        Boosters: []
 | 
					        Boosters: []
 | 
				
			||||||
 | 
					        /*circuitGameModes: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                uniqueName: "Survival",
 | 
				
			||||||
 | 
					                name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Survival", lang))
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                uniqueName: "VoidFlood",
 | 
				
			||||||
 | 
					                name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Corruption", lang))
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                uniqueName: "Excavation",
 | 
				
			||||||
 | 
					                name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Excavation", lang))
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                uniqueName: "Defense",
 | 
				
			||||||
 | 
					                name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Defense", lang))
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                uniqueName: "Exterminate",
 | 
				
			||||||
 | 
					                name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Exterminate", lang))
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                uniqueName: "Assassination",
 | 
				
			||||||
 | 
					                name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Assassination", lang))
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                uniqueName: "Alchemy",
 | 
				
			||||||
 | 
					                name: toTitleCase(getString("/Lotus/Language/Missions/MissionName_Alchemy", lang))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]*/
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
 | 
					    for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
 | 
				
			||||||
        res[item.productCategory].push({
 | 
					        res[item.productCategory].push({
 | 
				
			||||||
 | 
				
			|||||||
@ -23,9 +23,9 @@ export const setBoosterController: RequestHandler = async (req, res) => {
 | 
				
			|||||||
        res.status(400).send("Invalid ItemType provided.");
 | 
					        res.status(400).send("Invalid ItemType provided.");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const now = Math.floor(Date.now() / 1000);
 | 
					    const now = Math.trunc(Date.now() / 1000);
 | 
				
			||||||
    for (const { ItemType, ExpiryDate } of requests) {
 | 
					    for (const { ItemType, ExpiryDate } of requests) {
 | 
				
			||||||
        if (ExpiryDate < now) {
 | 
					        if (ExpiryDate <= now) {
 | 
				
			||||||
            // remove expired boosters
 | 
					            // remove expired boosters
 | 
				
			||||||
            const index = boosters.findIndex(item => item.ItemType === ItemType);
 | 
					            const index = boosters.findIndex(item => item.ItemType === ItemType);
 | 
				
			||||||
            if (index !== -1) {
 | 
					            if (index !== -1) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +0,0 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					 | 
				
			||||||
import { saveConfig } from "@/src/services/configWatcherService";
 | 
					 | 
				
			||||||
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
 | 
					 | 
				
			||||||
import { config, IConfig } from "@/src/services/configService";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const updateConfigDataController: RequestHandler = async (req, res) => {
 | 
					 | 
				
			||||||
    const account = await getAccountForRequest(req);
 | 
					 | 
				
			||||||
    if (isAdministrator(account)) {
 | 
					 | 
				
			||||||
        const data = req.body as IUpdateConfigDataRequest;
 | 
					 | 
				
			||||||
        config[data.key] = data.value;
 | 
					 | 
				
			||||||
        await saveConfig();
 | 
					 | 
				
			||||||
        res.end();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        res.status(401).end();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface IUpdateConfigDataRequest {
 | 
					 | 
				
			||||||
    key: keyof IConfig;
 | 
					 | 
				
			||||||
    value: never;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,15 +1,19 @@
 | 
				
			|||||||
import { RequestHandler } from "express";
 | 
					import { RequestHandler } from "express";
 | 
				
			||||||
import { getWorldState, populateFissures } from "@/src/services/worldStateService";
 | 
					import { getWorldState, populateDailyDeal, populateFissures } from "@/src/services/worldStateService";
 | 
				
			||||||
import { version_compare } from "@/src/helpers/inventoryHelpers";
 | 
					import { version_compare } from "@/src/helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const worldStateController: RequestHandler = async (req, res) => {
 | 
					export const worldStateController: RequestHandler = async (req, res) => {
 | 
				
			||||||
    const buildLabel = req.query.buildLabel as string | undefined;
 | 
					    const buildLabel = req.query.buildLabel as string | undefined;
 | 
				
			||||||
    const worldState = getWorldState(buildLabel);
 | 
					    const worldState = getWorldState(buildLabel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const populatePromises = [populateDailyDeal(worldState)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Omitting void fissures for versions prior to Dante Unbound to avoid script errors.
 | 
					    // Omitting void fissures for versions prior to Dante Unbound to avoid script errors.
 | 
				
			||||||
    if (!buildLabel || version_compare(buildLabel, "2024.03.24.20.00") >= 0) {
 | 
					    if (!buildLabel || version_compare(buildLabel, "2024.03.24.20.00") >= 0) {
 | 
				
			||||||
        await populateFissures(worldState);
 | 
					        populatePromises.push(populateFissures(worldState));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await Promise.all(populatePromises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.json(worldState);
 | 
					    res.json(worldState);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -21,13 +21,14 @@ import mongoose from "mongoose";
 | 
				
			|||||||
import { JSONStringify } from "json-with-bigint";
 | 
					import { JSONStringify } from "json-with-bigint";
 | 
				
			||||||
import { startWebServer } from "./services/webService";
 | 
					import { startWebServer } from "./services/webService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { validateConfig } from "@/src/services/configWatcherService";
 | 
					import { syncConfigWithDatabase, validateConfig } from "@/src/services/configWatcherService";
 | 
				
			||||||
import { updateWorldStateCollections } from "./services/worldStateService";
 | 
					import { updateWorldStateCollections } from "./services/worldStateService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Patch JSON.stringify to work flawlessly with Bigints.
 | 
					// Patch JSON.stringify to work flawlessly with Bigints.
 | 
				
			||||||
JSON.stringify = JSONStringify;
 | 
					JSON.stringify = JSONStringify;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
validateConfig();
 | 
					validateConfig();
 | 
				
			||||||
 | 
					syncConfigWithDatabase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mongoose
 | 
					mongoose
 | 
				
			||||||
    .connect(config.mongodbUrl)
 | 
					    .connect(config.mongodbUrl)
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,6 @@ export interface IMessageDatabase extends IMessage {
 | 
				
			|||||||
    ownerId: Types.ObjectId;
 | 
					    ownerId: Types.ObjectId;
 | 
				
			||||||
    date: Date; //created at
 | 
					    date: Date; //created at
 | 
				
			||||||
    attVisualOnly?: boolean;
 | 
					    attVisualOnly?: boolean;
 | 
				
			||||||
    expiry?: Date;
 | 
					 | 
				
			||||||
    _id: Types.ObjectId;
 | 
					    _id: Types.ObjectId;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,11 +27,13 @@ export interface IMessage {
 | 
				
			|||||||
    icon?: string;
 | 
					    icon?: string;
 | 
				
			||||||
    highPriority?: boolean;
 | 
					    highPriority?: boolean;
 | 
				
			||||||
    lowPrioNewPlayers?: boolean;
 | 
					    lowPrioNewPlayers?: boolean;
 | 
				
			||||||
    startDate?: Date;
 | 
					    transmission?: string;
 | 
				
			||||||
    endDate?: Date;
 | 
					 | 
				
			||||||
    att?: string[];
 | 
					    att?: string[];
 | 
				
			||||||
    countedAtt?: ITypeCount[];
 | 
					    countedAtt?: ITypeCount[];
 | 
				
			||||||
    transmission?: string;
 | 
					    startDate?: Date;
 | 
				
			||||||
 | 
					    endDate?: Date;
 | 
				
			||||||
 | 
					    goalTag?: string;
 | 
				
			||||||
 | 
					    CrossPlatform?: boolean;
 | 
				
			||||||
    arg?: Arg[];
 | 
					    arg?: Arg[];
 | 
				
			||||||
    gifts?: IGift[];
 | 
					    gifts?: IGift[];
 | 
				
			||||||
    r?: boolean;
 | 
					    r?: boolean;
 | 
				
			||||||
@ -107,7 +108,10 @@ const messageSchema = new Schema<IMessageDatabase>(
 | 
				
			|||||||
        lowPrioNewPlayers: Boolean,
 | 
					        lowPrioNewPlayers: Boolean,
 | 
				
			||||||
        startDate: Date,
 | 
					        startDate: Date,
 | 
				
			||||||
        endDate: Date,
 | 
					        endDate: Date,
 | 
				
			||||||
 | 
					        goalTag: String,
 | 
				
			||||||
 | 
					        date: { type: Date, required: true },
 | 
				
			||||||
        r: Boolean,
 | 
					        r: Boolean,
 | 
				
			||||||
 | 
					        CrossPlatform: Boolean,
 | 
				
			||||||
        att: { type: [String], default: undefined },
 | 
					        att: { type: [String], default: undefined },
 | 
				
			||||||
        gifts: { type: [giftSchema], default: undefined },
 | 
					        gifts: { type: [giftSchema], default: undefined },
 | 
				
			||||||
        countedAtt: { type: [typeCountSchema], default: undefined },
 | 
					        countedAtt: { type: [typeCountSchema], default: undefined },
 | 
				
			||||||
@ -128,7 +132,7 @@ const messageSchema = new Schema<IMessageDatabase>(
 | 
				
			|||||||
        declineAction: String,
 | 
					        declineAction: String,
 | 
				
			||||||
        hasAccountAction: Boolean
 | 
					        hasAccountAction: Boolean
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { timestamps: { createdAt: "date", updatedAt: false }, id: false }
 | 
					    { id: false }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
messageSchema.virtual("messageId").get(function (this: IMessageDatabase) {
 | 
					messageSchema.virtual("messageId").get(function (this: IMessageDatabase) {
 | 
				
			||||||
@ -151,13 +155,15 @@ messageSchema.set("toJSON", {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (messageDatabase.startDate && messageDatabase.endDate) {
 | 
					        if (messageDatabase.startDate && messageDatabase.endDate) {
 | 
				
			||||||
            messageClient.startDate = toMongoDate(messageDatabase.startDate);
 | 
					            messageClient.startDate = toMongoDate(messageDatabase.startDate);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            messageClient.endDate = toMongoDate(messageDatabase.endDate);
 | 
					            messageClient.endDate = toMongoDate(messageDatabase.endDate);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            delete messageClient.startDate;
 | 
				
			||||||
 | 
					            delete messageClient.endDate;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
messageSchema.index({ ownerId: 1 });
 | 
					messageSchema.index({ ownerId: 1 });
 | 
				
			||||||
messageSchema.index({ expiry: 1 }, { expireAfterSeconds: 0 });
 | 
					messageSchema.index({ endDate: 1 }, { expireAfterSeconds: 0 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Inbox = model<IMessageDatabase>("Inbox", messageSchema, "inbox");
 | 
					export const Inbox = model<IMessageDatabase>("Inbox", messageSchema, "inbox");
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { Document, HydratedDocument, Model, Schema, Types, model } from "mongoose";
 | 
					import { Document, Model, Schema, Types, model } from "mongoose";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    IFlavourItem,
 | 
					    IFlavourItem,
 | 
				
			||||||
    IRawUpgrade,
 | 
					    IRawUpgrade,
 | 
				
			||||||
@ -7,7 +7,6 @@ import {
 | 
				
			|||||||
    IBooster,
 | 
					    IBooster,
 | 
				
			||||||
    IInventoryClient,
 | 
					    IInventoryClient,
 | 
				
			||||||
    ISlots,
 | 
					    ISlots,
 | 
				
			||||||
    IMailboxDatabase,
 | 
					 | 
				
			||||||
    IDuviriInfo,
 | 
					    IDuviriInfo,
 | 
				
			||||||
    IPendingRecipeDatabase,
 | 
					    IPendingRecipeDatabase,
 | 
				
			||||||
    IPendingRecipeClient,
 | 
					    IPendingRecipeClient,
 | 
				
			||||||
@ -54,7 +53,6 @@ import {
 | 
				
			|||||||
    IUpgradeDatabase,
 | 
					    IUpgradeDatabase,
 | 
				
			||||||
    ICrewShipMemberDatabase,
 | 
					    ICrewShipMemberDatabase,
 | 
				
			||||||
    ICrewShipMemberClient,
 | 
					    ICrewShipMemberClient,
 | 
				
			||||||
    IMailboxClient,
 | 
					 | 
				
			||||||
    TEquipmentKey,
 | 
					    TEquipmentKey,
 | 
				
			||||||
    equipmentKeys,
 | 
					    equipmentKeys,
 | 
				
			||||||
    IKubrowPetDetailsDatabase,
 | 
					    IKubrowPetDetailsDatabase,
 | 
				
			||||||
@ -93,13 +91,17 @@ import {
 | 
				
			|||||||
    ICrewMemberSkillEfficiency,
 | 
					    ICrewMemberSkillEfficiency,
 | 
				
			||||||
    ICrewMemberDatabase,
 | 
					    ICrewMemberDatabase,
 | 
				
			||||||
    ICrewMemberClient,
 | 
					    ICrewMemberClient,
 | 
				
			||||||
    ISortieRewardAttenuation,
 | 
					    IRewardAttenuation,
 | 
				
			||||||
    IInvasionProgressDatabase,
 | 
					    IInvasionProgressDatabase,
 | 
				
			||||||
    IInvasionProgressClient,
 | 
					    IInvasionProgressClient,
 | 
				
			||||||
    IAccolades,
 | 
					    IAccolades,
 | 
				
			||||||
    IHubNpcCustomization,
 | 
					    IHubNpcCustomization,
 | 
				
			||||||
    ILotusCustomization,
 | 
					    ILotusCustomization,
 | 
				
			||||||
    IEndlessXpReward
 | 
					    IEndlessXpReward,
 | 
				
			||||||
 | 
					    IPersonalGoalProgressDatabase,
 | 
				
			||||||
 | 
					    IPersonalGoalProgressClient,
 | 
				
			||||||
 | 
					    IKubrowPetPrintClient,
 | 
				
			||||||
 | 
					    IKubrowPetPrintDatabase
 | 
				
			||||||
} from "../../types/inventoryTypes/inventoryTypes";
 | 
					} from "../../types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { IOid } from "../../types/commonTypes";
 | 
					import { IOid } from "../../types/commonTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -371,7 +373,7 @@ FlavourItemSchema.set("toJSON", {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MailboxSchema = new Schema<IMailboxDatabase>(
 | 
					/*const MailboxSchema = new Schema<IMailboxDatabase>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        LastInboxId: Schema.Types.ObjectId
 | 
					        LastInboxId: Schema.Types.ObjectId
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -384,7 +386,7 @@ MailboxSchema.set("toJSON", {
 | 
				
			|||||||
        delete mailboxDatabase.__v;
 | 
					        delete mailboxDatabase.__v;
 | 
				
			||||||
        (returnedObject as IMailboxClient).LastInboxId = toOid(mailboxDatabase.LastInboxId);
 | 
					        (returnedObject as IMailboxClient).LastInboxId = toOid(mailboxDatabase.LastInboxId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DuviriInfoSchema = new Schema<IDuviriInfo>(
 | 
					const DuviriInfoSchema = new Schema<IDuviriInfo>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -457,11 +459,35 @@ const discoveredMarkerSchema = new Schema<IDiscoveredMarker>(
 | 
				
			|||||||
    { _id: false }
 | 
					    { _id: false }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const personalGoalProgressSchema = new Schema<IPersonalGoalProgressDatabase>(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Best: Number,
 | 
				
			||||||
 | 
					        Count: Number,
 | 
				
			||||||
 | 
					        Tag: String,
 | 
				
			||||||
 | 
					        goalId: Types.ObjectId
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    { _id: false }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					personalGoalProgressSchema.set("toJSON", {
 | 
				
			||||||
 | 
					    virtuals: true,
 | 
				
			||||||
 | 
					    transform(_doc, obj) {
 | 
				
			||||||
 | 
					        const db = obj as IPersonalGoalProgressDatabase;
 | 
				
			||||||
 | 
					        const client = obj as IPersonalGoalProgressClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        client._id = toOid(db.goalId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        delete obj.goalId;
 | 
				
			||||||
 | 
					        delete obj.__v;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const challengeProgressSchema = new Schema<IChallengeProgress>(
 | 
					const challengeProgressSchema = new Schema<IChallengeProgress>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Progress: Number,
 | 
					        Progress: Number,
 | 
				
			||||||
        Name: String,
 | 
					        Completed: { type: [String], default: undefined },
 | 
				
			||||||
        Completed: [String]
 | 
					        ReceivedJunctionReward: Boolean,
 | 
				
			||||||
 | 
					        Name: { type: String, required: true }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { _id: false }
 | 
					    { _id: false }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@ -984,6 +1010,27 @@ const traitsSchema = new Schema<ITraits>(
 | 
				
			|||||||
    { _id: false }
 | 
					    { _id: false }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const kubrowPetPrintSchema = new Schema<IKubrowPetPrintDatabase>({
 | 
				
			||||||
 | 
					    ItemType: String,
 | 
				
			||||||
 | 
					    Name: String,
 | 
				
			||||||
 | 
					    IsMale: Boolean,
 | 
				
			||||||
 | 
					    Size: Number,
 | 
				
			||||||
 | 
					    DominantTraits: traitsSchema,
 | 
				
			||||||
 | 
					    RecessiveTraits: traitsSchema
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					kubrowPetPrintSchema.set("toJSON", {
 | 
				
			||||||
 | 
					    virtuals: true,
 | 
				
			||||||
 | 
					    transform(_doc, obj) {
 | 
				
			||||||
 | 
					        const db = obj as IKubrowPetPrintDatabase;
 | 
				
			||||||
 | 
					        const client = obj as IKubrowPetPrintClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        client.ItemId = toOid(db._id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        delete obj._id;
 | 
				
			||||||
 | 
					        delete obj.__v;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const detailsSchema = new Schema<IKubrowPetDetailsDatabase>(
 | 
					const detailsSchema = new Schema<IKubrowPetDetailsDatabase>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Name: String,
 | 
					        Name: String,
 | 
				
			||||||
@ -1370,10 +1417,10 @@ lastSortieRewardSchema.set("toJSON", {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sortieRewardAttenutationSchema = new Schema<ISortieRewardAttenuation>(
 | 
					const rewardAttenutationSchema = new Schema<IRewardAttenuation>(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Tag: String,
 | 
					        Tag: { type: String, required: true },
 | 
				
			||||||
        Atten: Number
 | 
					        Atten: { type: Number, required: true }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { _id: false }
 | 
					    { _id: false }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@ -1487,7 +1534,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        KubrowPetEggs: [kubrowPetEggSchema],
 | 
					        KubrowPetEggs: [kubrowPetEggSchema],
 | 
				
			||||||
        //Prints   Cat(3 Prints)\Kubrow(2 Prints) Pets
 | 
					        //Prints   Cat(3 Prints)\Kubrow(2 Prints) Pets
 | 
				
			||||||
        //KubrowPetPrints: [Schema.Types.Mixed],
 | 
					        KubrowPetPrints: [kubrowPetPrintSchema],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //Item for EquippedGear example:Scaner,LoadoutTechSummon etc
 | 
					        //Item for EquippedGear example:Scaner,LoadoutTechSummon etc
 | 
				
			||||||
        Consumables: [typeCountSchema],
 | 
					        Consumables: [typeCountSchema],
 | 
				
			||||||
@ -1601,6 +1648,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
        PendingSpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined },
 | 
					        PendingSpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined },
 | 
				
			||||||
        SpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined },
 | 
					        SpectreLoadouts: { type: [spectreLoadoutsSchema], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //Darvo Deal
 | 
				
			||||||
 | 
					        UsedDailyDeals: [String],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //New Quest Email
 | 
					        //New Quest Email
 | 
				
			||||||
        EmailItems: [typeCountSchema],
 | 
					        EmailItems: [typeCountSchema],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1616,7 +1666,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
        CompletedSorties: [String],
 | 
					        CompletedSorties: [String],
 | 
				
			||||||
        LastSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
					        LastSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
				
			||||||
        LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
					        LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined },
 | 
				
			||||||
        SortieRewardAttenuation: { type: [sortieRewardAttenutationSchema], default: undefined },
 | 
					        SortieRewardAttenuation: { type: [rewardAttenutationSchema], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Resource Extractor Drones
 | 
					        // Resource Extractor Drones
 | 
				
			||||||
        Drones: [droneSchema],
 | 
					        Drones: [droneSchema],
 | 
				
			||||||
@ -1630,7 +1680,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
        //CompletedJobs: [Schema.Types.Mixed],
 | 
					        //CompletedJobs: [Schema.Types.Mixed],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //Game mission\ivent score example  "Tag": "WaterFight", "Best": 170, "Count": 1258,
 | 
					        //Game mission\ivent score example  "Tag": "WaterFight", "Best": 170, "Count": 1258,
 | 
				
			||||||
        //PersonalGoalProgress: [Schema.Types.Mixed],
 | 
					        PersonalGoalProgress: { type: [personalGoalProgressSchema], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //Setting interface Style
 | 
					        //Setting interface Style
 | 
				
			||||||
        ThemeStyle: String,
 | 
					        ThemeStyle: String,
 | 
				
			||||||
@ -1701,7 +1751,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
        //Unknown and system
 | 
					        //Unknown and system
 | 
				
			||||||
        DuviriInfo: DuviriInfoSchema,
 | 
					        DuviriInfo: DuviriInfoSchema,
 | 
				
			||||||
        LastInventorySync: Schema.Types.ObjectId,
 | 
					        LastInventorySync: Schema.Types.ObjectId,
 | 
				
			||||||
        Mailbox: MailboxSchema,
 | 
					        //Mailbox: MailboxSchema,
 | 
				
			||||||
        HandlerPoints: Number,
 | 
					        HandlerPoints: Number,
 | 
				
			||||||
        ChallengesFixVersion: Number,
 | 
					        ChallengesFixVersion: Number,
 | 
				
			||||||
        PlayedParkourTutorial: Boolean,
 | 
					        PlayedParkourTutorial: Boolean,
 | 
				
			||||||
@ -1717,7 +1767,6 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
        //ChallengeInstanceStates: [Schema.Types.Mixed],
 | 
					        //ChallengeInstanceStates: [Schema.Types.Mixed],
 | 
				
			||||||
        RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined },
 | 
					        RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined },
 | 
				
			||||||
        //Robotics: [Schema.Types.Mixed],
 | 
					        //Robotics: [Schema.Types.Mixed],
 | 
				
			||||||
        //UsedDailyDeals: [Schema.Types.Mixed],
 | 
					 | 
				
			||||||
        CollectibleSeries: { type: [collectibleEntrySchema], default: undefined },
 | 
					        CollectibleSeries: { type: [collectibleEntrySchema], default: undefined },
 | 
				
			||||||
        HasResetAccount: { type: Boolean, default: false },
 | 
					        HasResetAccount: { type: Boolean, default: false },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1754,7 +1803,11 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
 | 
				
			|||||||
        BrandedSuits: { type: [Schema.Types.ObjectId], default: undefined },
 | 
					        BrandedSuits: { type: [Schema.Types.ObjectId], default: undefined },
 | 
				
			||||||
        LockedWeaponGroup: { type: lockedWeaponGroupSchema, default: undefined },
 | 
					        LockedWeaponGroup: { type: lockedWeaponGroupSchema, default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        HubNpcCustomizations: { type: [hubNpcCustomizationSchema], default: undefined }
 | 
					        HubNpcCustomizations: { type: [hubNpcCustomizationSchema], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ClaimedJunctionChallengeRewards: { type: [String], default: undefined },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SpecialItemRewardAttenuation: { type: [rewardAttenutationSchema], default: undefined }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { timestamps: { createdAt: "Created", updatedAt: false } }
 | 
					    { timestamps: { createdAt: "Created", updatedAt: false } }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
@ -1824,6 +1877,7 @@ export type InventoryDocumentProps = {
 | 
				
			|||||||
    CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
 | 
					    CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
 | 
				
			||||||
    PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
 | 
					    PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
 | 
				
			||||||
    CrewMembers: Types.DocumentArray<ICrewMemberDatabase>;
 | 
					    CrewMembers: Types.DocumentArray<ICrewMemberDatabase>;
 | 
				
			||||||
 | 
					    KubrowPetPrints: Types.DocumentArray<IKubrowPetPrintDatabase>;
 | 
				
			||||||
} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
 | 
					} & { [K in TEquipmentKey]: Types.DocumentArray<IEquipmentDatabase> };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
 | 
					// eslint-disable-next-line @typescript-eslint/no-empty-object-type
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,8 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
 | 
				
			|||||||
        LastLogin: { type: Date, default: 0 },
 | 
					        LastLogin: { type: Date, default: 0 },
 | 
				
			||||||
        LatestEventMessageDate: { type: Date, default: 0 },
 | 
					        LatestEventMessageDate: { type: Date, default: 0 },
 | 
				
			||||||
        LastLoginRewardDate: { type: Number, default: 0 },
 | 
					        LastLoginRewardDate: { type: Number, default: 0 },
 | 
				
			||||||
        LoginDays: { type: Number, default: 1 }
 | 
					        LoginDays: { type: Number, default: 1 },
 | 
				
			||||||
 | 
					        DailyFirstWinDate: { type: Number, default: 0 }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    opts
 | 
					    opts
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
				
			|||||||
@ -40,6 +40,7 @@ const placedDecosSchema = new Schema<IPlacedDecosDatabase>(
 | 
				
			|||||||
        Pos: [Number],
 | 
					        Pos: [Number],
 | 
				
			||||||
        Rot: [Number],
 | 
					        Rot: [Number],
 | 
				
			||||||
        Scale: Number,
 | 
					        Scale: Number,
 | 
				
			||||||
 | 
					        Sockets: Number,
 | 
				
			||||||
        PictureFrameInfo: { type: pictureFrameInfoSchema, default: undefined }
 | 
					        PictureFrameInfo: { type: pictureFrameInfoSchema, default: undefined }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    { id: false }
 | 
					    { id: false }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { IFissureDatabase } from "@/src/types/worldStateTypes";
 | 
					import { IDailyDealDatabase, IFissureDatabase } from "@/src/types/worldStateTypes";
 | 
				
			||||||
import { model, Schema } from "mongoose";
 | 
					import { model, Schema } from "mongoose";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fissureSchema = new Schema<IFissureDatabase>({
 | 
					const fissureSchema = new Schema<IFissureDatabase>({
 | 
				
			||||||
@ -12,3 +12,19 @@ const fissureSchema = new Schema<IFissureDatabase>({
 | 
				
			|||||||
fissureSchema.index({ Expiry: 1 }, { expireAfterSeconds: 0 }); // With this, MongoDB will automatically delete expired entries.
 | 
					fissureSchema.index({ Expiry: 1 }, { expireAfterSeconds: 0 }); // With this, MongoDB will automatically delete expired entries.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Fissure = model<IFissureDatabase>("Fissure", fissureSchema);
 | 
					export const Fissure = model<IFissureDatabase>("Fissure", fissureSchema);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const dailyDealSchema = new Schema<IDailyDealDatabase>({
 | 
				
			||||||
 | 
					    StoreItem: { type: String, required: true },
 | 
				
			||||||
 | 
					    Activation: { type: Date, required: true },
 | 
				
			||||||
 | 
					    Expiry: { type: Date, required: true },
 | 
				
			||||||
 | 
					    Discount: { type: Number, required: true },
 | 
				
			||||||
 | 
					    OriginalPrice: { type: Number, required: true },
 | 
				
			||||||
 | 
					    SalePrice: { type: Number, required: true },
 | 
				
			||||||
 | 
					    AmountTotal: { type: Number, required: true },
 | 
				
			||||||
 | 
					    AmountSold: { type: Number, required: true }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dailyDealSchema.index({ StoreItem: 1 }, { unique: true });
 | 
				
			||||||
 | 
					dailyDealSchema.index({ Expiry: 1 }, { expireAfterSeconds: 86400 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DailyDeal = model<IDailyDealDatabase>("DailyDeal", dailyDealSchema);
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@ import { changeDojoRootController } from "@/src/controllers/api/changeDojoRootCo
 | 
				
			|||||||
import { changeGuildRankController } from "@/src/controllers/api/changeGuildRankController";
 | 
					import { changeGuildRankController } from "@/src/controllers/api/changeGuildRankController";
 | 
				
			||||||
import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
 | 
					import { checkDailyMissionBonusController } from "@/src/controllers/api/checkDailyMissionBonusController";
 | 
				
			||||||
import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
 | 
					import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompletedRecipeController";
 | 
				
			||||||
 | 
					import { claimJunctionChallengeRewardController } from "@/src/controllers/api/claimJunctionChallengeRewardController";
 | 
				
			||||||
import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
 | 
					import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
 | 
				
			||||||
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
 | 
					import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
 | 
				
			||||||
import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController";
 | 
					import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController";
 | 
				
			||||||
@ -237,6 +238,7 @@ apiRouter.post("/artifacts.php", artifactsController);
 | 
				
			|||||||
apiRouter.post("/artifactTransmutation.php", artifactTransmutationController);
 | 
					apiRouter.post("/artifactTransmutation.php", artifactTransmutationController);
 | 
				
			||||||
apiRouter.post("/changeDojoRoot.php", changeDojoRootController);
 | 
					apiRouter.post("/changeDojoRoot.php", changeDojoRootController);
 | 
				
			||||||
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
 | 
					apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
 | 
				
			||||||
 | 
					apiRouter.post("/claimJunctionChallengeReward.php", claimJunctionChallengeRewardController);
 | 
				
			||||||
apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
 | 
					apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
 | 
				
			||||||
apiRouter.post("/clearNewEpisodeReward.php", clearNewEpisodeRewardController);
 | 
					apiRouter.post("/clearNewEpisodeReward.php", clearNewEpisodeRewardController);
 | 
				
			||||||
apiRouter.post("/commitStoryModeDecision.php", (_req, res) => { res.end(); }); // U14 (maybe wanna actually unlock the ship features?)
 | 
					apiRouter.post("/commitStoryModeDecision.php", (_req, res) => { res.end(); }); // U14 (maybe wanna actually unlock the ship features?)
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@ import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAl
 | 
				
			|||||||
import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
 | 
					import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
 | 
				
			||||||
import { webuiFileChangeDetectedController } from "@/src/controllers/custom/webuiFileChangeDetectedController";
 | 
					import { webuiFileChangeDetectedController } from "@/src/controllers/custom/webuiFileChangeDetectedController";
 | 
				
			||||||
import { completeAllMissionsController } from "@/src/controllers/custom/completeAllMissionsController";
 | 
					import { completeAllMissionsController } from "@/src/controllers/custom/completeAllMissionsController";
 | 
				
			||||||
 | 
					import { addMissingHelminthBlueprintsController } from "@/src/controllers/custom/addMissingHelminthBlueprintsController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
 | 
					import { createAccountController } from "@/src/controllers/custom/createAccountController";
 | 
				
			||||||
import { createMessageController } from "@/src/controllers/custom/createMessageController";
 | 
					import { createMessageController } from "@/src/controllers/custom/createMessageController";
 | 
				
			||||||
@ -24,8 +25,7 @@ import { manageQuestsController } from "@/src/controllers/custom/manageQuestsCon
 | 
				
			|||||||
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
 | 
					import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
 | 
				
			||||||
import { setBoosterController } from "@/src/controllers/custom/setBoosterController";
 | 
					import { setBoosterController } from "@/src/controllers/custom/setBoosterController";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
 | 
					import { getConfigController, setConfigController } from "@/src/controllers/custom/configController";
 | 
				
			||||||
import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const customRouter = express.Router();
 | 
					const customRouter = express.Router();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -42,6 +42,7 @@ customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
 | 
				
			|||||||
customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
 | 
					customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
 | 
				
			||||||
customRouter.get("/webuiFileChangeDetected", webuiFileChangeDetectedController);
 | 
					customRouter.get("/webuiFileChangeDetected", webuiFileChangeDetectedController);
 | 
				
			||||||
customRouter.get("/completeAllMissions", completeAllMissionsController);
 | 
					customRouter.get("/completeAllMissions", completeAllMissionsController);
 | 
				
			||||||
 | 
					customRouter.get("/addMissingHelminthBlueprints", addMissingHelminthBlueprintsController);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
customRouter.post("/createAccount", createAccountController);
 | 
					customRouter.post("/createAccount", createAccountController);
 | 
				
			||||||
customRouter.post("/createMessage", createMessageController);
 | 
					customRouter.post("/createMessage", createMessageController);
 | 
				
			||||||
@ -53,7 +54,7 @@ customRouter.post("/manageQuests", manageQuestsController);
 | 
				
			|||||||
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
 | 
					customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
 | 
				
			||||||
customRouter.post("/setBooster", setBoosterController);
 | 
					customRouter.post("/setBooster", setBoosterController);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
customRouter.get("/config", getConfigDataController);
 | 
					customRouter.post("/getConfig", getConfigController);
 | 
				
			||||||
customRouter.post("/config", updateConfigDataController);
 | 
					customRouter.post("/setConfig", setConfigController);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { customRouter };
 | 
					export { customRouter };
 | 
				
			||||||
 | 
				
			|||||||
@ -49,6 +49,8 @@ export interface IConfig {
 | 
				
			|||||||
    noDeathMarks?: boolean;
 | 
					    noDeathMarks?: boolean;
 | 
				
			||||||
    noKimCooldowns?: boolean;
 | 
					    noKimCooldowns?: boolean;
 | 
				
			||||||
    fullyStockedVendors?: boolean;
 | 
					    fullyStockedVendors?: boolean;
 | 
				
			||||||
 | 
					    baroAlwaysAvailable?: boolean;
 | 
				
			||||||
 | 
					    baroFullyStocked?: boolean;
 | 
				
			||||||
    syndicateMissionsRepeatable?: boolean;
 | 
					    syndicateMissionsRepeatable?: boolean;
 | 
				
			||||||
    unlockAllProfitTakerStages?: boolean;
 | 
					    unlockAllProfitTakerStages?: boolean;
 | 
				
			||||||
    instantFinishRivenChallenge?: boolean;
 | 
					    instantFinishRivenChallenge?: boolean;
 | 
				
			||||||
@ -63,6 +65,7 @@ export interface IConfig {
 | 
				
			|||||||
    fastClanAscension?: boolean;
 | 
					    fastClanAscension?: boolean;
 | 
				
			||||||
    missionsCanGiveAllRelics?: boolean;
 | 
					    missionsCanGiveAllRelics?: boolean;
 | 
				
			||||||
    unlockAllSimarisResearchEntries?: boolean;
 | 
					    unlockAllSimarisResearchEntries?: boolean;
 | 
				
			||||||
 | 
					    disableDailyTribute?: boolean;
 | 
				
			||||||
    spoofMasteryRank?: number;
 | 
					    spoofMasteryRank?: number;
 | 
				
			||||||
    nightwaveStandingMultiplier?: number;
 | 
					    nightwaveStandingMultiplier?: number;
 | 
				
			||||||
    unfaithfulBugFixes?: {
 | 
					    unfaithfulBugFixes?: {
 | 
				
			||||||
@ -74,11 +77,14 @@ export interface IConfig {
 | 
				
			|||||||
        affinityBoost?: boolean;
 | 
					        affinityBoost?: boolean;
 | 
				
			||||||
        resourceBoost?: boolean;
 | 
					        resourceBoost?: boolean;
 | 
				
			||||||
        starDays?: boolean;
 | 
					        starDays?: boolean;
 | 
				
			||||||
 | 
					        galleonOfGhouls?: number;
 | 
				
			||||||
        eidolonOverride?: string;
 | 
					        eidolonOverride?: string;
 | 
				
			||||||
        vallisOverride?: string;
 | 
					        vallisOverride?: string;
 | 
				
			||||||
        duviriOverride?: string;
 | 
					        duviriOverride?: string;
 | 
				
			||||||
        nightwaveOverride?: string;
 | 
					        nightwaveOverride?: string;
 | 
				
			||||||
 | 
					        allTheFissures?: string;
 | 
				
			||||||
        circuitGameModes?: string[];
 | 
					        circuitGameModes?: string[];
 | 
				
			||||||
 | 
					        darvoStockMultiplier?: number;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    dev?: {
 | 
					    dev?: {
 | 
				
			||||||
        keepVendorsExpired?: boolean;
 | 
					        keepVendorsExpired?: boolean;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,12 @@
 | 
				
			|||||||
import fs from "fs";
 | 
					import chokidar from "chokidar";
 | 
				
			||||||
import fsPromises from "fs/promises";
 | 
					import fsPromises from "fs/promises";
 | 
				
			||||||
import { logger } from "../utils/logger";
 | 
					import { logger } from "../utils/logger";
 | 
				
			||||||
import { config, configPath, loadConfig } from "./configService";
 | 
					import { config, configPath, loadConfig } from "./configService";
 | 
				
			||||||
import { getWebPorts, sendWsBroadcast, startWebServer, stopWebServer } from "./webService";
 | 
					import { getWebPorts, sendWsBroadcast, startWebServer, stopWebServer } from "./webService";
 | 
				
			||||||
 | 
					import { Inbox } from "../models/inboxModel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let amnesia = false;
 | 
					let amnesia = false;
 | 
				
			||||||
fs.watchFile(configPath, (now, then) => {
 | 
					chokidar.watch(configPath).on("change", () => {
 | 
				
			||||||
    // https://github.com/oven-sh/bun/issues/20542
 | 
					 | 
				
			||||||
    if (process.versions.bun && now.mtimeMs == then.mtimeMs) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (amnesia) {
 | 
					    if (amnesia) {
 | 
				
			||||||
        amnesia = false;
 | 
					        amnesia = false;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
@ -22,6 +18,7 @@ fs.watchFile(configPath, (now, then) => {
 | 
				
			|||||||
            process.exit(1);
 | 
					            process.exit(1);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        validateConfig();
 | 
					        validateConfig();
 | 
				
			||||||
 | 
					        syncConfigWithDatabase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const webPorts = getWebPorts();
 | 
					        const webPorts = getWebPorts();
 | 
				
			||||||
        if (config.httpPort != webPorts.http || config.httpsPort != webPorts.https) {
 | 
					        if (config.httpPort != webPorts.http || config.httpsPort != webPorts.https) {
 | 
				
			||||||
@ -51,6 +48,15 @@ export const validateConfig = (): void => {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					        config.worldState?.galleonOfGhouls &&
 | 
				
			||||||
 | 
					        config.worldState.galleonOfGhouls != 1 &&
 | 
				
			||||||
 | 
					        config.worldState.galleonOfGhouls != 2 &&
 | 
				
			||||||
 | 
					        config.worldState.galleonOfGhouls != 3
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        config.worldState.galleonOfGhouls = 0;
 | 
				
			||||||
 | 
					        modified = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (modified) {
 | 
					    if (modified) {
 | 
				
			||||||
        logger.info(`Updating config file to fix some issues with it.`);
 | 
					        logger.info(`Updating config file to fix some issues with it.`);
 | 
				
			||||||
        void saveConfig();
 | 
					        void saveConfig();
 | 
				
			||||||
@ -61,3 +67,10 @@ export const saveConfig = async (): Promise<void> => {
 | 
				
			|||||||
    amnesia = true;
 | 
					    amnesia = true;
 | 
				
			||||||
    await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2));
 | 
					    await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -349,7 +349,8 @@ export const removeDojoDeco = (
 | 
				
			|||||||
            component.DecoCapacity! += meta.capacityCost;
 | 
					            component.DecoCapacity! += meta.capacityCost;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const itemType = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type)![0];
 | 
					        const [itemType, meta] = Object.entries(ExportResources).find(arr => arr[1].deco == deco.Type)!;
 | 
				
			||||||
 | 
					        component.DecoCapacity! += meta.dojoCapacityCost!;
 | 
				
			||||||
        if (deco.Sockets !== undefined) {
 | 
					        if (deco.Sockets !== undefined) {
 | 
				
			||||||
            addVaultFusionTreasures(guild, [
 | 
					            addVaultFusionTreasures(guild, [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
				
			|||||||
@ -296,6 +296,12 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
 | 
				
			|||||||
            db[key] = client[key];
 | 
					            db[key] = client[key];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    // IRewardAtten[]
 | 
				
			||||||
 | 
					    for (const key of ["SortieRewardAttenuation", "SpecialItemRewardAttenuation"] as const) {
 | 
				
			||||||
 | 
					        if (client[key] !== undefined) {
 | 
				
			||||||
 | 
					            db[key] = client[key];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (client.XPInfo !== undefined) {
 | 
					    if (client.XPInfo !== undefined) {
 | 
				
			||||||
        db.XPInfo = client.XPInfo;
 | 
					        db.XPInfo = client.XPInfo;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -2,8 +2,8 @@ import { IMessageDatabase, Inbox } from "@/src/models/inboxModel";
 | 
				
			|||||||
import { getAccountForRequest } from "@/src/services/loginService";
 | 
					import { getAccountForRequest } from "@/src/services/loginService";
 | 
				
			||||||
import { HydratedDocument, Types } from "mongoose";
 | 
					import { HydratedDocument, Types } from "mongoose";
 | 
				
			||||||
import { Request } from "express";
 | 
					import { Request } from "express";
 | 
				
			||||||
import eventMessages from "@/static/fixed_responses/eventMessages.json";
 | 
					import { unixTimesInMs } from "../constants/timeConstants";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					import { config } from "./configService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getAllMessagesSorted = async (accountId: string): Promise<HydratedDocument<IMessageDatabase>[]> => {
 | 
					export const getAllMessagesSorted = async (accountId: string): Promise<HydratedDocument<IMessageDatabase>[]> => {
 | 
				
			||||||
    const inbox = await Inbox.find({ ownerId: accountId }).sort({ date: -1 });
 | 
					    const inbox = await Inbox.find({ ownerId: accountId }).sort({ date: -1 });
 | 
				
			||||||
@ -29,40 +29,72 @@ export const deleteAllMessagesRead = async (accountId: string): Promise<void> =>
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const createNewEventMessages = async (req: Request): Promise<void> => {
 | 
					export const createNewEventMessages = async (req: Request): Promise<void> => {
 | 
				
			||||||
    const account = await getAccountForRequest(req);
 | 
					    const account = await getAccountForRequest(req);
 | 
				
			||||||
    const latestEventMessageDate = account.LatestEventMessageDate;
 | 
					    const newEventMessages: IMessageCreationTemplate[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //TODO: is baroo there? create these kind of messages too (periodical messages)
 | 
					    // Baro
 | 
				
			||||||
    const newEventMessages = eventMessages.Messages.filter(m => new Date(m.eventMessageDate) > latestEventMessageDate);
 | 
					    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) {
 | 
				
			||||||
 | 
					        newEventMessages.push({
 | 
				
			||||||
 | 
					            sndr: "/Lotus/Language/G1Quests/VoidTraderName",
 | 
				
			||||||
 | 
					            sub: "/Lotus/Language/CommunityMessages/VoidTraderAppearanceTitle",
 | 
				
			||||||
 | 
					            msg: "/Lotus/Language/CommunityMessages/VoidTraderAppearanceMessage",
 | 
				
			||||||
 | 
					            icon: "/Lotus/Interface/Icons/Npcs/BaroKiTeerPortrait.png",
 | 
				
			||||||
 | 
					            startDate: new Date(baroActualStart),
 | 
				
			||||||
 | 
					            endDate: new Date(baroStart + unixTimesInMs.day * 14),
 | 
				
			||||||
 | 
					            CrossPlatform: true,
 | 
				
			||||||
 | 
					            arg: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Key: "NODE_NAME",
 | 
				
			||||||
 | 
					                    Tag: ["EarthHUB", "MercuryHUB", "SaturnHUB", "PlutoHUB"][baroIndex % 4]
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            date: new Date(baroActualStart)
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // BUG: Deleting the inbox message manually means it'll just be automatically re-created. This is because we don't use startDate/endDate for these config-toggled events.
 | 
				
			||||||
 | 
					    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"
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (newEventMessages.length === 0) {
 | 
					    if (newEventMessages.length === 0) {
 | 
				
			||||||
        logger.debug(`No new event messages. Latest event message date: ${latestEventMessageDate.toISOString()}`);
 | 
					 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const savedEventMessages = await createMessage(account._id, newEventMessages);
 | 
					    await createMessage(account._id, newEventMessages);
 | 
				
			||||||
    logger.debug("created event messages", savedEventMessages);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const latestEventMessage = newEventMessages.reduce((prev, current) =>
 | 
					    const latestEventMessage = newEventMessages.reduce((prev, current) =>
 | 
				
			||||||
        prev.eventMessageDate > current.eventMessageDate ? prev : current
 | 
					        prev.startDate! > current.startDate! ? prev : current
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					    account.LatestEventMessageDate = new Date(latestEventMessage.startDate!);
 | 
				
			||||||
    account.LatestEventMessageDate = new Date(latestEventMessage.eventMessageDate);
 | 
					 | 
				
			||||||
    await account.save();
 | 
					    await account.save();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const createMessage = async (
 | 
					export const createMessage = async (
 | 
				
			||||||
    accountId: string | Types.ObjectId,
 | 
					    accountId: string | Types.ObjectId,
 | 
				
			||||||
    messages: IMessageCreationTemplate[]
 | 
					    messages: IMessageCreationTemplate[]
 | 
				
			||||||
): Promise<HydratedDocument<IMessageDatabase>[]> => {
 | 
					): Promise<void> => {
 | 
				
			||||||
    const ownerIdMessages = messages.map(m => ({
 | 
					    const ownerIdMessages = messages.map(m => ({
 | 
				
			||||||
        ...m,
 | 
					        ...m,
 | 
				
			||||||
 | 
					        date: m.date ?? new Date(),
 | 
				
			||||||
        ownerId: accountId
 | 
					        ownerId: accountId
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
 | 
					    await Inbox.insertMany(ownerIdMessages);
 | 
				
			||||||
    const savedMessages = await Inbox.insertMany(ownerIdMessages);
 | 
					 | 
				
			||||||
    return savedMessages as HydratedDocument<IMessageDatabase>[];
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IMessageCreationTemplate extends Omit<IMessageDatabase, "_id" | "date" | "ownerId"> {
 | 
					export interface IMessageCreationTemplate extends Omit<IMessageDatabase, "_id" | "date" | "ownerId"> {
 | 
				
			||||||
    ownerId?: string;
 | 
					    date?: Date;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -29,7 +29,8 @@ import {
 | 
				
			|||||||
    ICalendarProgress,
 | 
					    ICalendarProgress,
 | 
				
			||||||
    INemesisWeaponTargetFingerprint,
 | 
					    INemesisWeaponTargetFingerprint,
 | 
				
			||||||
    INemesisPetTargetFingerprint,
 | 
					    INemesisPetTargetFingerprint,
 | 
				
			||||||
    IDialogueDatabase
 | 
					    IDialogueDatabase,
 | 
				
			||||||
 | 
					    IKubrowPetPrintClient
 | 
				
			||||||
} from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					} from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
 | 
					import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
 | 
				
			||||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
 | 
					import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
 | 
				
			||||||
@ -43,6 +44,7 @@ import {
 | 
				
			|||||||
} from "../types/inventoryTypes/commonInventoryTypes";
 | 
					} from "../types/inventoryTypes/commonInventoryTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ExportArcanes,
 | 
					    ExportArcanes,
 | 
				
			||||||
 | 
					    ExportBoosters,
 | 
				
			||||||
    ExportBundles,
 | 
					    ExportBundles,
 | 
				
			||||||
    ExportChallenges,
 | 
					    ExportChallenges,
 | 
				
			||||||
    ExportCustoms,
 | 
					    ExportCustoms,
 | 
				
			||||||
@ -424,7 +426,6 @@ export const addItem = async (
 | 
				
			|||||||
                    ItemType: "/Lotus/Types/Game/KubrowPet/Eggs/KubrowEgg",
 | 
					                    ItemType: "/Lotus/Types/Game/KubrowPet/Eggs/KubrowEgg",
 | 
				
			||||||
                    _id: new Types.ObjectId()
 | 
					                    _id: new Types.ObjectId()
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                inventory.KubrowPetEggs ??= [];
 | 
					 | 
				
			||||||
                inventory.KubrowPetEggs.push(egg);
 | 
					                inventory.KubrowPetEggs.push(egg);
 | 
				
			||||||
                changes.push({
 | 
					                changes.push({
 | 
				
			||||||
                    ItemType: egg.ItemType,
 | 
					                    ItemType: egg.ItemType,
 | 
				
			||||||
@ -499,6 +500,7 @@ export const addItem = async (
 | 
				
			|||||||
        // - Blueprints for Ancient Protector Specter, Shield Osprey Specter, etc. have num=1 despite giving their purchaseQuantity.
 | 
					        // - Blueprints for Ancient Protector Specter, Shield Osprey Specter, etc. have num=1 despite giving their purchaseQuantity.
 | 
				
			||||||
        if (!exactQuantity) {
 | 
					        if (!exactQuantity) {
 | 
				
			||||||
            quantity *= ExportGear[typeName].purchaseQuantity ?? 1;
 | 
					            quantity *= ExportGear[typeName].purchaseQuantity ?? 1;
 | 
				
			||||||
 | 
					            logger.debug(`non-exact acquisition of ${typeName}; factored quantity is ${quantity}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const consumablesChanges = [
 | 
					        const consumablesChanges = [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -670,6 +672,17 @@ export const addItem = async (
 | 
				
			|||||||
        return await addEmailItem(inventory, typeName);
 | 
					        return await addEmailItem(inventory, typeName);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Boosters are an odd case. They're only added like this via Baro's Void Surplus afaik.
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const boosterEntry = Object.entries(ExportBoosters).find(arr => arr[1].typeName == typeName);
 | 
				
			||||||
 | 
					        if (boosterEntry) {
 | 
				
			||||||
 | 
					            addBooster(typeName, quantity, inventory);
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                Boosters: [{ ItemType: typeName, ExpiryDate: quantity }]
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Path-based duck typing
 | 
					    // Path-based duck typing
 | 
				
			||||||
    switch (typeName.substr(1).split("/")[1]) {
 | 
					    switch (typeName.substr(1).split("/")[1]) {
 | 
				
			||||||
        case "Powersuits":
 | 
					        case "Powersuits":
 | 
				
			||||||
@ -783,7 +796,11 @@ export const addItem = async (
 | 
				
			|||||||
                        typeName.substr(1).split("/")[3] == "CatbrowPet" ||
 | 
					                        typeName.substr(1).split("/")[3] == "CatbrowPet" ||
 | 
				
			||||||
                        typeName.substr(1).split("/")[3] == "KubrowPet"
 | 
					                        typeName.substr(1).split("/")[3] == "KubrowPet"
 | 
				
			||||||
                    ) {
 | 
					                    ) {
 | 
				
			||||||
                        if (typeName != "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem") {
 | 
					                        if (
 | 
				
			||||||
 | 
					                            typeName != "/Lotus/Types/Game/KubrowPet/Eggs/KubrowPetEggItem" &&
 | 
				
			||||||
 | 
					                            typeName != "/Lotus/Types/Game/KubrowPet/BlankTraitPrint" &&
 | 
				
			||||||
 | 
					                            typeName != "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint"
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
                            return addKubrowPet(inventory, typeName, undefined, premiumPurchase);
 | 
					                            return addKubrowPet(inventory, typeName, undefined, premiumPurchase);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    } else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) {
 | 
					                    } else if (typeName.startsWith("/Lotus/Types/Game/CrewShip/CrewMember/")) {
 | 
				
			||||||
@ -1047,8 +1064,13 @@ export const addKubrowPet = (
 | 
				
			|||||||
    const configs: IItemConfig[] = applyDefaultUpgrades(inventory, kubrowPet?.defaultUpgrades);
 | 
					    const configs: IItemConfig[] = applyDefaultUpgrades(inventory, kubrowPet?.defaultUpgrades);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!details) {
 | 
					    if (!details) {
 | 
				
			||||||
        let traits: ITraits;
 | 
					        const isCatbrow = [
 | 
				
			||||||
 | 
					            "/Lotus/Types/Game/CatbrowPet/CheshireCatbrowPetPowerSuit",
 | 
				
			||||||
 | 
					            "/Lotus/Types/Game/CatbrowPet/MirrorCatbrowPetPowerSuit",
 | 
				
			||||||
 | 
					            "/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit"
 | 
				
			||||||
 | 
					        ].includes(kubrowPetName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let traits: ITraits;
 | 
				
			||||||
        if (kubrowPetName == "/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit") {
 | 
					        if (kubrowPetName == "/Lotus/Types/Game/CatbrowPet/VampireCatbrowPetPowerSuit") {
 | 
				
			||||||
            traits = {
 | 
					            traits = {
 | 
				
			||||||
                BaseColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseVampire",
 | 
					                BaseColor: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseVampire",
 | 
				
			||||||
@ -1063,12 +1085,7 @@ export const addKubrowPet = (
 | 
				
			|||||||
                Tail: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailVampire"
 | 
					                Tail: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailVampire"
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            const isCatbrow = [
 | 
					 | 
				
			||||||
                "/Lotus/Types/Game/CatbrowPet/MirrorCatbrowPetPowerSuit",
 | 
					 | 
				
			||||||
                "/Lotus/Types/Game/CatbrowPet/CheshireCatbrowPetPowerSuit"
 | 
					 | 
				
			||||||
            ].includes(kubrowPetName);
 | 
					 | 
				
			||||||
            const traitsPool = isCatbrow ? catbrowDetails : kubrowDetails;
 | 
					            const traitsPool = isCatbrow ? catbrowDetails : kubrowDetails;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            traits = {
 | 
					            traits = {
 | 
				
			||||||
                BaseColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
 | 
					                BaseColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
 | 
				
			||||||
                SecondaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
 | 
					                SecondaryColor: getRandomWeightedReward(traitsPool.Colors, kubrowWeights)!.type,
 | 
				
			||||||
@ -1087,7 +1104,7 @@ export const addKubrowPet = (
 | 
				
			|||||||
            Name: "",
 | 
					            Name: "",
 | 
				
			||||||
            IsPuppy: !premiumPurchase,
 | 
					            IsPuppy: !premiumPurchase,
 | 
				
			||||||
            HasCollar: true,
 | 
					            HasCollar: true,
 | 
				
			||||||
            PrintsRemaining: 3,
 | 
					            PrintsRemaining: isCatbrow ? 3 : 2,
 | 
				
			||||||
            Status: premiumPurchase ? Status.StatusStasis : Status.StatusIncubating,
 | 
					            Status: premiumPurchase ? Status.StatusStasis : Status.StatusIncubating,
 | 
				
			||||||
            HatchDate: premiumPurchase ? new Date() : new Date(Date.now() + 10 * unixTimesInMs.hour), // On live, this seems to be somewhat randomised so that the pet hatches 9~11 hours after start.
 | 
					            HatchDate: premiumPurchase ? new Date() : new Date(Date.now() + 10 * unixTimesInMs.hour), // On live, this seems to be somewhat randomised so that the pet hatches 9~11 hours after start.
 | 
				
			||||||
            IsMale: !!getRandomInt(0, 1),
 | 
					            IsMale: !!getRandomInt(0, 1),
 | 
				
			||||||
@ -1111,6 +1128,26 @@ export const addKubrowPet = (
 | 
				
			|||||||
    return inventoryChanges;
 | 
					    return inventoryChanges;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const addKubrowPetPrint = (
 | 
				
			||||||
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
 | 
					    pet: IEquipmentDatabase,
 | 
				
			||||||
 | 
					    inventoryChanges: IInventoryChanges
 | 
				
			||||||
 | 
					): void => {
 | 
				
			||||||
 | 
					    inventoryChanges.KubrowPetPrints ??= [];
 | 
				
			||||||
 | 
					    inventoryChanges.KubrowPetPrints.push(
 | 
				
			||||||
 | 
					        inventory.KubrowPetPrints[
 | 
				
			||||||
 | 
					            inventory.KubrowPetPrints.push({
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint",
 | 
				
			||||||
 | 
					                Name: pet.Details!.Name,
 | 
				
			||||||
 | 
					                IsMale: pet.Details!.IsMale,
 | 
				
			||||||
 | 
					                Size: pet.Details!.Size,
 | 
				
			||||||
 | 
					                DominantTraits: pet.Details!.DominantTraits,
 | 
				
			||||||
 | 
					                RecessiveTraits: pet.Details!.RecessiveTraits
 | 
				
			||||||
 | 
					            }) - 1
 | 
				
			||||||
 | 
					        ].toJSON<IKubrowPetPrintClient>()
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const updateSlots = (
 | 
					export const updateSlots = (
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    slotName: SlotNames,
 | 
					    slotName: SlotNames,
 | 
				
			||||||
@ -1329,7 +1366,7 @@ export const addCustomization = (
 | 
				
			|||||||
    customizationName: string,
 | 
					    customizationName: string,
 | 
				
			||||||
    inventoryChanges: IInventoryChanges = {}
 | 
					    inventoryChanges: IInventoryChanges = {}
 | 
				
			||||||
): IInventoryChanges => {
 | 
					): IInventoryChanges => {
 | 
				
			||||||
    if (!inventory.FlavourItems.find(x => x.ItemType == customizationName)) {
 | 
					    if (!inventory.FlavourItems.some(x => x.ItemType == customizationName)) {
 | 
				
			||||||
        const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1;
 | 
					        const flavourItemIndex = inventory.FlavourItems.push({ ItemType: customizationName }) - 1;
 | 
				
			||||||
        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
					        // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | 
				
			||||||
        inventoryChanges.FlavourItems ??= [];
 | 
					        inventoryChanges.FlavourItems ??= [];
 | 
				
			||||||
@ -1345,7 +1382,7 @@ export const addSkin = (
 | 
				
			|||||||
    typeName: string,
 | 
					    typeName: string,
 | 
				
			||||||
    inventoryChanges: IInventoryChanges = {}
 | 
					    inventoryChanges: IInventoryChanges = {}
 | 
				
			||||||
): IInventoryChanges => {
 | 
					): IInventoryChanges => {
 | 
				
			||||||
    if (inventory.WeaponSkins.find(x => x.ItemType == typeName)) {
 | 
					    if (inventory.WeaponSkins.some(x => x.ItemType == typeName)) {
 | 
				
			||||||
        logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
 | 
					        logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;
 | 
					        const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;
 | 
				
			||||||
@ -1831,6 +1868,15 @@ export const addChallenges = (
 | 
				
			|||||||
    return affiliationMods;
 | 
					    return affiliationMods;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const addCalendarProgress = (inventory: TInventoryDatabaseDocument, value: { challenge: string }[]): void => {
 | 
				
			||||||
 | 
					    const calendarProgress = getCalendarProgress(inventory);
 | 
				
			||||||
 | 
					    const currentSeason = getWorldState().KnownCalendarSeasons[0];
 | 
				
			||||||
 | 
					    calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = currentSeason.Days.findIndex(
 | 
				
			||||||
 | 
					        day => day.events.length != 0 && day.events[0].challenge == value[value.length - 1].challenge
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    checkCalendarChallengeCompletion(calendarProgress, currentSeason);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes, Tier }: IMission): void => {
 | 
					export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes, Tier }: IMission): void => {
 | 
				
			||||||
    const { Missions } = inventory;
 | 
					    const { Missions } = inventory;
 | 
				
			||||||
    const itemIndex = Missions.findIndex(item => item.Tag === Tag);
 | 
					    const itemIndex = Missions.findIndex(item => item.Tag === Tag);
 | 
				
			||||||
 | 
				
			|||||||
@ -144,7 +144,8 @@ export const claimLoginReward = async (
 | 
				
			|||||||
        case "RT_STORE_ITEM":
 | 
					        case "RT_STORE_ITEM":
 | 
				
			||||||
        case "RT_RECIPE":
 | 
					        case "RT_RECIPE":
 | 
				
			||||||
        case "RT_RANDOM_RECIPE":
 | 
					        case "RT_RANDOM_RECIPE":
 | 
				
			||||||
            return (await handleStoreItemAcquisition(reward.StoreItemType, inventory, reward.Amount)).InventoryChanges;
 | 
					            return (await handleStoreItemAcquisition(reward.StoreItemType, inventory, reward.Amount, undefined, true))
 | 
				
			||||||
 | 
					                .InventoryChanges;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        case "RT_CREDITS":
 | 
					        case "RT_CREDITS":
 | 
				
			||||||
            return updateCurrency(inventory, -reward.Amount, false);
 | 
					            return updateCurrency(inventory, -reward.Amount, false);
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ import { IRngResult, SRng, getRandomElement, getRandomReward } from "@/src/servi
 | 
				
			|||||||
import { equipmentKeys, IMission, ITypeCount, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { equipmentKeys, IMission, ITypeCount, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    addBooster,
 | 
					    addBooster,
 | 
				
			||||||
 | 
					    addCalendarProgress,
 | 
				
			||||||
    addChallenges,
 | 
					    addChallenges,
 | 
				
			||||||
    addConsumables,
 | 
					    addConsumables,
 | 
				
			||||||
    addCrewShipAmmo,
 | 
					    addCrewShipAmmo,
 | 
				
			||||||
@ -33,10 +34,8 @@ import {
 | 
				
			|||||||
    addSkin,
 | 
					    addSkin,
 | 
				
			||||||
    addStanding,
 | 
					    addStanding,
 | 
				
			||||||
    applyClientEquipmentUpdates,
 | 
					    applyClientEquipmentUpdates,
 | 
				
			||||||
    checkCalendarChallengeCompletion,
 | 
					 | 
				
			||||||
    combineInventoryChanges,
 | 
					    combineInventoryChanges,
 | 
				
			||||||
    generateRewardSeed,
 | 
					    generateRewardSeed,
 | 
				
			||||||
    getCalendarProgress,
 | 
					 | 
				
			||||||
    getDialogue,
 | 
					    getDialogue,
 | 
				
			||||||
    giveNemesisPetRecipe,
 | 
					    giveNemesisPetRecipe,
 | 
				
			||||||
    giveNemesisWeaponRecipe,
 | 
					    giveNemesisWeaponRecipe,
 | 
				
			||||||
@ -46,7 +45,7 @@ import {
 | 
				
			|||||||
import { updateQuestKey } from "@/src/services/questService";
 | 
					import { updateQuestKey } from "@/src/services/questService";
 | 
				
			||||||
import { Types } from "mongoose";
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
import { IAffiliationMods, IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
					import { IAffiliationMods, IInventoryChanges } from "@/src/types/purchaseTypes";
 | 
				
			||||||
import { fromStoreItem, getLevelKeyRewards, toStoreItem } from "@/src/services/itemDataService";
 | 
					import { fromStoreItem, getLevelKeyRewards, isStoreItem, toStoreItem } from "@/src/services/itemDataService";
 | 
				
			||||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
 | 
					import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
 | 
				
			||||||
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
 | 
					import { getEntriesUnsafe } from "@/src/utils/ts-utils";
 | 
				
			||||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
					import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
 | 
				
			||||||
@ -235,7 +234,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    for (const [key, value] of getEntriesUnsafe(inventoryUpdates)) {
 | 
					    for (const [key, value] of getEntriesUnsafe(inventoryUpdates)) {
 | 
				
			||||||
        if (value === undefined) {
 | 
					        if (value === undefined) {
 | 
				
			||||||
            logger.error(`Inventory update key ${key} has no value `);
 | 
					            logger.error(`Inventory update key ${key} has no value`);
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        switch (key) {
 | 
					        switch (key) {
 | 
				
			||||||
@ -482,7 +481,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                                    msg: "/Lotus/Language/G1Quests/DeathMarkMessage",
 | 
					                                    msg: "/Lotus/Language/G1Quests/DeathMarkMessage",
 | 
				
			||||||
                                    icon: "/Lotus/Interface/Icons/Npcs/Stalker_d.png",
 | 
					                                    icon: "/Lotus/Interface/Icons/Npcs/Stalker_d.png",
 | 
				
			||||||
                                    highPriority: true,
 | 
					                                    highPriority: true,
 | 
				
			||||||
                                    expiry: new Date(Date.now() + 86400_000) // TOVERIFY: This type of inbox message seems to automatically delete itself. We'll just delete it after 24 hours, but it's clear if this is correct.
 | 
					                                    endDate: new Date(Date.now() + 86400_000) // TOVERIFY: This type of inbox message seems to automatically delete itself. We'll just delete it after 24 hours, but it's not clear if this is correct.
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            ]);
 | 
					                            ]);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
@ -526,7 +525,6 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            case "KubrowPetEggs": {
 | 
					            case "KubrowPetEggs": {
 | 
				
			||||||
                for (const egg of value) {
 | 
					                for (const egg of value) {
 | 
				
			||||||
                    inventory.KubrowPetEggs ??= [];
 | 
					 | 
				
			||||||
                    inventory.KubrowPetEggs.push({
 | 
					                    inventory.KubrowPetEggs.push({
 | 
				
			||||||
                        ItemType: egg.ItemType,
 | 
					                        ItemType: egg.ItemType,
 | 
				
			||||||
                        _id: new Types.ObjectId()
 | 
					                        _id: new Types.ObjectId()
 | 
				
			||||||
@ -609,6 +607,47 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                inventoryChanges.RegularCredits -= value;
 | 
					                inventoryChanges.RegularCredits -= value;
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            case "GoalProgress": {
 | 
				
			||||||
 | 
					                for (const uploadProgress of value) {
 | 
				
			||||||
 | 
					                    const goal = getWorldState().Goals.find(x => x._id.$oid == uploadProgress._id.$oid);
 | 
				
			||||||
 | 
					                    if (goal && goal.Personal) {
 | 
				
			||||||
 | 
					                        inventory.PersonalGoalProgress ??= [];
 | 
				
			||||||
 | 
					                        const goalProgress = inventory.PersonalGoalProgress.find(x => x.goalId.equals(goal._id.$oid));
 | 
				
			||||||
 | 
					                        if (goalProgress) {
 | 
				
			||||||
 | 
					                            goalProgress.Best = Math.max(goalProgress.Best, uploadProgress.Best);
 | 
				
			||||||
 | 
					                            goalProgress.Count += uploadProgress.Count;
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            inventory.PersonalGoalProgress.push({
 | 
				
			||||||
 | 
					                                Best: uploadProgress.Best,
 | 
				
			||||||
 | 
					                                Count: uploadProgress.Count,
 | 
				
			||||||
 | 
					                                Tag: goal.Tag,
 | 
				
			||||||
 | 
					                                goalId: new Types.ObjectId(goal._id.$oid)
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (
 | 
				
			||||||
 | 
					                                goal.Reward &&
 | 
				
			||||||
 | 
					                                goal.Reward.items &&
 | 
				
			||||||
 | 
					                                goal.MissionKeyName &&
 | 
				
			||||||
 | 
					                                goal.MissionKeyName in goalMessagesByKey
 | 
				
			||||||
 | 
					                            ) {
 | 
				
			||||||
 | 
					                                // Send reward via inbox
 | 
				
			||||||
 | 
					                                const info = goalMessagesByKey[goal.MissionKeyName];
 | 
				
			||||||
 | 
					                                await createMessage(inventory.accountOwnerId, [
 | 
				
			||||||
 | 
					                                    {
 | 
				
			||||||
 | 
					                                        sndr: info.sndr,
 | 
				
			||||||
 | 
					                                        msg: info.msg,
 | 
				
			||||||
 | 
					                                        att: goal.Reward.items.map(x => (isStoreItem(x) ? fromStoreItem(x) : x)),
 | 
				
			||||||
 | 
					                                        sub: info.sub,
 | 
				
			||||||
 | 
					                                        icon: info.icon,
 | 
				
			||||||
 | 
					                                        highPriority: true
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                ]);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            case "InvasionProgress": {
 | 
					            case "InvasionProgress": {
 | 
				
			||||||
                for (const clientProgress of value) {
 | 
					                for (const clientProgress of value) {
 | 
				
			||||||
                    const dbProgress = inventory.QualifyingInvasions.find(x =>
 | 
					                    const dbProgress = inventory.QualifyingInvasions.find(x =>
 | 
				
			||||||
@ -630,12 +669,7 @@ export const addMissionInventoryUpdates = async (
 | 
				
			|||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case "CalendarProgress": {
 | 
					            case "CalendarProgress": {
 | 
				
			||||||
                const calendarProgress = getCalendarProgress(inventory);
 | 
					                addCalendarProgress(inventory, value);
 | 
				
			||||||
                const currentSeason = getWorldState().KnownCalendarSeasons[0];
 | 
					 | 
				
			||||||
                calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = currentSeason.Days.findIndex(
 | 
					 | 
				
			||||||
                    x => x.events[0].challenge == value[value.length - 1].challenge
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                checkCalendarChallengeCompletion(calendarProgress, currentSeason);
 | 
					 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case "duviriCaveOffers": {
 | 
					            case "duviriCaveOffers": {
 | 
				
			||||||
@ -927,6 +961,7 @@ const droptableAliases: Record<string, string> = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//TODO: return type of partial missioninventoryupdate response
 | 
					//TODO: return type of partial missioninventoryupdate response
 | 
				
			||||||
export const addMissionRewards = async (
 | 
					export const addMissionRewards = async (
 | 
				
			||||||
 | 
					    account: TAccountDocument,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        wagerTier: wagerTier,
 | 
					        wagerTier: wagerTier,
 | 
				
			||||||
@ -962,17 +997,29 @@ export const addMissionRewards = async (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let missionCompletionCredits = 0;
 | 
					    let missionCompletionCredits = 0;
 | 
				
			||||||
    //inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
 | 
					    //inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (rewardInfo.goalId) {
 | 
				
			||||||
 | 
					        const goal = getWorldState().Goals.find(x => x._id.$oid == rewardInfo.goalId);
 | 
				
			||||||
 | 
					        if (goal?.MissionKeyName) {
 | 
				
			||||||
 | 
					            levelKeyName = goal.MissionKeyName;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (levelKeyName) {
 | 
					    if (levelKeyName) {
 | 
				
			||||||
        const fixedLevelRewards = getLevelKeyRewards(levelKeyName);
 | 
					        const fixedLevelRewards = getLevelKeyRewards(levelKeyName);
 | 
				
			||||||
        //logger.debug(`fixedLevelRewards ${fixedLevelRewards}`);
 | 
					        //logger.debug(`fixedLevelRewards ${fixedLevelRewards}`);
 | 
				
			||||||
        if (fixedLevelRewards.levelKeyRewards) {
 | 
					        if (fixedLevelRewards.levelKeyRewards) {
 | 
				
			||||||
            addFixedLevelRewards(fixedLevelRewards.levelKeyRewards, inventory, MissionRewards, rewardInfo);
 | 
					            missionCompletionCredits += addFixedLevelRewards(
 | 
				
			||||||
 | 
					                fixedLevelRewards.levelKeyRewards,
 | 
				
			||||||
 | 
					                MissionRewards,
 | 
				
			||||||
 | 
					                rewardInfo
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (fixedLevelRewards.levelKeyRewards2) {
 | 
					        if (fixedLevelRewards.levelKeyRewards2) {
 | 
				
			||||||
            for (const reward of fixedLevelRewards.levelKeyRewards2) {
 | 
					            for (const reward of fixedLevelRewards.levelKeyRewards2) {
 | 
				
			||||||
                //quest stage completion credit rewards
 | 
					                //quest stage completion credit rewards
 | 
				
			||||||
                if (reward.rewardType == "RT_CREDITS") {
 | 
					                if (reward.rewardType == "RT_CREDITS") {
 | 
				
			||||||
                    missionCompletionCredits += reward.amount; // will be added to inventory in addCredits
 | 
					                    missionCompletionCredits += reward.amount;
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                MissionRewards.push({
 | 
					                MissionRewards.push({
 | 
				
			||||||
@ -1001,12 +1048,11 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            const levelCreditReward = getLevelCreditRewards(node);
 | 
					            const levelCreditReward = getLevelCreditRewards(node);
 | 
				
			||||||
            missionCompletionCredits += levelCreditReward;
 | 
					            missionCompletionCredits += levelCreditReward;
 | 
				
			||||||
            inventory.RegularCredits += levelCreditReward;
 | 
					 | 
				
			||||||
            logger.debug(`levelCreditReward ${levelCreditReward}`);
 | 
					            logger.debug(`levelCreditReward ${levelCreditReward}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (node.missionReward) {
 | 
					        if (node.missionReward) {
 | 
				
			||||||
            missionCompletionCredits += addFixedLevelRewards(node.missionReward, inventory, MissionRewards, rewardInfo);
 | 
					            missionCompletionCredits += addFixedLevelRewards(node.missionReward, MissionRewards, rewardInfo);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (rewardInfo.sortieTag == "Mission1") {
 | 
					        if (rewardInfo.sortieTag == "Mission1") {
 | 
				
			||||||
@ -1116,7 +1162,9 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        combineInventoryChanges(inventoryChanges, inventoryChange.InventoryChanges);
 | 
					        combineInventoryChanges(inventoryChanges, inventoryChange.InventoryChanges);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const credits = addCredits(inventory, {
 | 
					    inventory.RegularCredits += missionCompletionCredits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const credits = await addCredits(account, inventory, {
 | 
				
			||||||
        missionCompletionCredits,
 | 
					        missionCompletionCredits,
 | 
				
			||||||
        missionDropCredits: creditDrops ?? 0,
 | 
					        missionDropCredits: creditDrops ?? 0,
 | 
				
			||||||
        rngRewardCredits: inventoryChanges.RegularCredits ?? 0
 | 
					        rngRewardCredits: inventoryChanges.RegularCredits ?? 0
 | 
				
			||||||
@ -1207,7 +1255,7 @@ export const addMissionRewards = async (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
 | 
					    if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
 | 
				
			||||||
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
					        // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
        const [jobType, unkIndex, hubNode, syndicateMissionId, locationTag] = rewardInfo.jobId.split("_");
 | 
					        const [jobType, unkIndex, hubNode, syndicateMissionId] = rewardInfo.jobId.split("_");
 | 
				
			||||||
        const syndicateMissions: ISyndicateMissionInfo[] = [];
 | 
					        const syndicateMissions: ISyndicateMissionInfo[] = [];
 | 
				
			||||||
        if (syndicateMissionId) {
 | 
					        if (syndicateMissionId) {
 | 
				
			||||||
            pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
 | 
					            pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
 | 
				
			||||||
@ -1216,12 +1264,27 @@ export const addMissionRewards = async (
 | 
				
			|||||||
        if (syndicateEntry && syndicateEntry.Jobs) {
 | 
					        if (syndicateEntry && syndicateEntry.Jobs) {
 | 
				
			||||||
            let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
 | 
					            let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
 | 
				
			||||||
            if (syndicateEntry.Tag === "EntratiSyndicate") {
 | 
					            if (syndicateEntry.Tag === "EntratiSyndicate") {
 | 
				
			||||||
                if (jobType.endsWith("VaultBounty")) {
 | 
					                if (
 | 
				
			||||||
                    const vault = syndicateEntry.Jobs.find(j => j.locationTag === locationTag);
 | 
					                    [
 | 
				
			||||||
                    if (vault) currentJob = vault;
 | 
					                        "DeimosRuinsExterminateBounty",
 | 
				
			||||||
 | 
					                        "DeimosRuinsEscortBounty",
 | 
				
			||||||
 | 
					                        "DeimosRuinsMistBounty",
 | 
				
			||||||
 | 
					                        "DeimosRuinsPurifyBounty",
 | 
				
			||||||
 | 
					                        "DeimosRuinsSacBounty",
 | 
				
			||||||
 | 
					                        "VaultBounty"
 | 
				
			||||||
 | 
					                    ].some(ending => jobType.endsWith(ending))
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
 | 
					                    const vault = syndicateEntry.Jobs.find(j => j.locationTag == rewardInfo.jobId!.split("_").at(-1));
 | 
				
			||||||
 | 
					                    if (vault) {
 | 
				
			||||||
 | 
					                        currentJob = vault;
 | 
				
			||||||
 | 
					                        if (jobType.endsWith("VaultBounty")) {
 | 
				
			||||||
 | 
					                            currentJob.xpAmounts = [currentJob.xpAmounts.reduce((partialSum, a) => partialSum + a, 0)];
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                let medallionAmount = Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1));
 | 
					                let medallionAmount = Math.floor(
 | 
				
			||||||
 | 
					                    Math.min(rewardInfo.JobStage, currentJob.xpAmounts.length - 1) / (rewardInfo.Q ? 0.8 : 1)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
                if (
 | 
					                if (
 | 
				
			||||||
                    ["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some(
 | 
					                    ["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some(
 | 
				
			||||||
                        ending => jobType.endsWith(ending)
 | 
					                        ending => jobType.endsWith(ending)
 | 
				
			||||||
@ -1324,48 +1387,61 @@ export const addMissionRewards = async (
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//creditBonus is not entirely accurate.
 | 
					export const addCredits = async (
 | 
				
			||||||
//TODO: consider ActiveBoosters
 | 
					    account: TAccountDocument,
 | 
				
			||||||
export const addCredits = (
 | 
					 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        missionDropCredits,
 | 
					        missionDropCredits,
 | 
				
			||||||
        missionCompletionCredits,
 | 
					        missionCompletionCredits,
 | 
				
			||||||
        rngRewardCredits
 | 
					        rngRewardCredits
 | 
				
			||||||
    }: { missionDropCredits: number; missionCompletionCredits: number; rngRewardCredits: number }
 | 
					    }: { missionDropCredits: number; missionCompletionCredits: number; rngRewardCredits: number }
 | 
				
			||||||
): IMissionCredits => {
 | 
					): Promise<IMissionCredits> => {
 | 
				
			||||||
    const hasDailyCreditBonus = true;
 | 
					 | 
				
			||||||
    const totalCredits = missionDropCredits + missionCompletionCredits + rngRewardCredits;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const finalCredits: IMissionCredits = {
 | 
					    const finalCredits: IMissionCredits = {
 | 
				
			||||||
        MissionCredits: [missionDropCredits, missionDropCredits],
 | 
					        MissionCredits: [missionDropCredits, missionDropCredits],
 | 
				
			||||||
        CreditBonus: [missionCompletionCredits, missionCompletionCredits],
 | 
					        CreditsBonus: [missionCompletionCredits, missionCompletionCredits],
 | 
				
			||||||
        TotalCredits: [totalCredits, totalCredits]
 | 
					        TotalCredits: [0, 0]
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (hasDailyCreditBonus) {
 | 
					    const today = Math.trunc(Date.now() / 86400000) * 86400;
 | 
				
			||||||
 | 
					    if (account.DailyFirstWinDate != today) {
 | 
				
			||||||
 | 
					        account.DailyFirstWinDate = today;
 | 
				
			||||||
 | 
					        await account.save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logger.debug(`daily first win, doubling missionCompletionCredits (${missionCompletionCredits})`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        finalCredits.DailyMissionBonus = true;
 | 
				
			||||||
        inventory.RegularCredits += missionCompletionCredits;
 | 
					        inventory.RegularCredits += missionCompletionCredits;
 | 
				
			||||||
        finalCredits.CreditBonus[1] *= 2;
 | 
					        finalCredits.CreditsBonus[1] *= 2;
 | 
				
			||||||
        finalCredits.MissionCredits[1] *= 2;
 | 
					 | 
				
			||||||
        finalCredits.TotalCredits[1] *= 2;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!hasDailyCreditBonus) {
 | 
					    const totalCredits = finalCredits.MissionCredits[1] + finalCredits.CreditsBonus[1] + rngRewardCredits;
 | 
				
			||||||
        return finalCredits;
 | 
					    finalCredits.TotalCredits = [totalCredits, totalCredits];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (config.worldState?.creditBoost) {
 | 
				
			||||||
 | 
					        inventory.RegularCredits += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					        finalCredits.TotalCredits[1] += finalCredits.TotalCredits[1];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return { ...finalCredits, DailyMissionBonus: true };
 | 
					    const now = Math.trunc(Date.now() / 1000); // TOVERIFY: Should we maybe subtract mission time as to apply credit boosters that expired during mission?
 | 
				
			||||||
 | 
					    if ((inventory.Boosters.find(x => x.ItemType == "/Lotus/Types/Boosters/CreditBooster")?.ExpiryDate ?? 0) > now) {
 | 
				
			||||||
 | 
					        inventory.RegularCredits += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					        finalCredits.TotalCredits[1] += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if ((inventory.Boosters.find(x => x.ItemType == "/Lotus/Types/Boosters/CreditBlessing")?.ExpiryDate ?? 0) > now) {
 | 
				
			||||||
 | 
					        inventory.RegularCredits += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					        finalCredits.TotalCredits[1] += finalCredits.TotalCredits[1];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return finalCredits;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const addFixedLevelRewards = (
 | 
					export const addFixedLevelRewards = (
 | 
				
			||||||
    rewards: IMissionRewardExternal,
 | 
					    rewards: IMissionRewardExternal,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					 | 
				
			||||||
    MissionRewards: IMissionReward[],
 | 
					    MissionRewards: IMissionReward[],
 | 
				
			||||||
    rewardInfo?: IRewardInfo
 | 
					    rewardInfo?: IRewardInfo
 | 
				
			||||||
): number => {
 | 
					): number => {
 | 
				
			||||||
    let missionBonusCredits = 0;
 | 
					    let missionBonusCredits = 0;
 | 
				
			||||||
    if (rewards.credits) {
 | 
					    if (rewards.credits) {
 | 
				
			||||||
        missionBonusCredits += rewards.credits;
 | 
					        missionBonusCredits += rewards.credits;
 | 
				
			||||||
        inventory.RegularCredits += rewards.credits;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (rewards.items) {
 | 
					    if (rewards.items) {
 | 
				
			||||||
        for (const item of rewards.items) {
 | 
					        for (const item of rewards.items) {
 | 
				
			||||||
@ -1539,6 +1615,27 @@ function getRandomMissionDrops(
 | 
				
			|||||||
                    ? "/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriKullervoSteelPathRNGRewards"
 | 
					                    ? "/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriKullervoSteelPathRNGRewards"
 | 
				
			||||||
                    : "/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriKullervoNormalRNGRewards"
 | 
					                    : "/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriKullervoNormalRNGRewards"
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
 | 
					        } else if (RewardInfo.T == 17) {
 | 
				
			||||||
 | 
					            if (mission?.Tier == 1) {
 | 
				
			||||||
 | 
					                logger.warn(`non-steel path duviri murmur tier used on steel path?!`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            /*if (operation eight claw is active) {
 | 
				
			||||||
 | 
					                drops.push({
 | 
				
			||||||
 | 
					                    StoreItem: "/Lotus/StoreItems/Types/Gameplay/DuviriMITW/Resources/DuviriMurmurItemEvent",
 | 
				
			||||||
 | 
					                    ItemCount: 10
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }*/
 | 
				
			||||||
 | 
					            rewardManifests = ["/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriMurmurFinalChestRewards"];
 | 
				
			||||||
 | 
					        } else if (RewardInfo.T == 19) {
 | 
				
			||||||
 | 
					            /*if (operation eight claw is active) {
 | 
				
			||||||
 | 
					                drops.push({
 | 
				
			||||||
 | 
					                    StoreItem: "/Lotus/StoreItems/Types/Gameplay/DuviriMITW/Resources/DuviriMurmurItemEvent",
 | 
				
			||||||
 | 
					                    ItemCount: 15
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }*/
 | 
				
			||||||
 | 
					            rewardManifests = [
 | 
				
			||||||
 | 
					                "/Lotus/Types/Game/MissionDecks/DuviriEncounterRewards/DuviriMurmurFinalSteelChestRewards"
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
        } else if (RewardInfo.T == 70) {
 | 
					        } else if (RewardInfo.T == 70) {
 | 
				
			||||||
            // Orowyrm chest, gives 10 Pathos Clamps, or 15 on Steel Path.
 | 
					            // Orowyrm chest, gives 10 Pathos Clamps, or 15 on Steel Path.
 | 
				
			||||||
            drops.push({
 | 
					            drops.push({
 | 
				
			||||||
@ -1554,7 +1651,7 @@ function getRandomMissionDrops(
 | 
				
			|||||||
        if (RewardInfo.jobId) {
 | 
					        if (RewardInfo.jobId) {
 | 
				
			||||||
            if (RewardInfo.JobStage! >= 0) {
 | 
					            if (RewardInfo.JobStage! >= 0) {
 | 
				
			||||||
                // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
					                // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
				
			||||||
                const [jobType, unkIndex, hubNode, syndicateMissionId, locationTag] = RewardInfo.jobId.split("_");
 | 
					                const [jobType, unkIndex, hubNode, syndicateMissionId] = RewardInfo.jobId.split("_");
 | 
				
			||||||
                let isEndlessJob = false;
 | 
					                let isEndlessJob = false;
 | 
				
			||||||
                if (syndicateMissionId) {
 | 
					                if (syndicateMissionId) {
 | 
				
			||||||
                    const syndicateMissions: ISyndicateMissionInfo[] = [];
 | 
					                    const syndicateMissions: ISyndicateMissionInfo[] = [];
 | 
				
			||||||
@ -1566,21 +1663,30 @@ function getRandomMissionDrops(
 | 
				
			|||||||
                        let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
 | 
					                        let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (syndicateEntry.Tag === "EntratiSyndicate") {
 | 
					                        if (syndicateEntry.Tag === "EntratiSyndicate") {
 | 
				
			||||||
                            if (jobType.endsWith("VaultBounty")) {
 | 
					                            if (
 | 
				
			||||||
                                const vault = syndicateEntry.Jobs.find(j => j.locationTag === locationTag);
 | 
					                                [
 | 
				
			||||||
                                if (vault) job = vault;
 | 
					                                    "DeimosRuinsExterminateBounty",
 | 
				
			||||||
 | 
					                                    "DeimosRuinsEscortBounty",
 | 
				
			||||||
 | 
					                                    "DeimosRuinsMistBounty",
 | 
				
			||||||
 | 
					                                    "DeimosRuinsPurifyBounty",
 | 
				
			||||||
 | 
					                                    "DeimosRuinsSacBounty",
 | 
				
			||||||
 | 
					                                    "VaultBounty"
 | 
				
			||||||
 | 
					                                ].some(ending => jobType.endsWith(ending))
 | 
				
			||||||
 | 
					                            ) {
 | 
				
			||||||
 | 
					                                const vault = syndicateEntry.Jobs.find(
 | 
				
			||||||
 | 
					                                    j => j.locationTag === RewardInfo.jobId!.split("_").at(-1)
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					                                if (vault) {
 | 
				
			||||||
 | 
					                                    job = vault;
 | 
				
			||||||
 | 
					                                    if (jobType.endsWith("VaultBounty")) {
 | 
				
			||||||
 | 
					                                        job.rewards = job.rewards.replace(
 | 
				
			||||||
 | 
					                                            "/Lotus/Types/Game/MissionDecks/",
 | 
				
			||||||
 | 
					                                            "/Supplementals/"
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
 | 
					                                        job.xpAmounts = [job.xpAmounts.reduce((partialSum, a) => partialSum + a, 0)];
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            // if (
 | 
					 | 
				
			||||||
                            //     [
 | 
					 | 
				
			||||||
                            //         "DeimosRuinsExterminateBounty",
 | 
					 | 
				
			||||||
                            //         "DeimosRuinsEscortBounty",
 | 
					 | 
				
			||||||
                            //         "DeimosRuinsMistBounty",
 | 
					 | 
				
			||||||
                            //         "DeimosRuinsPurifyBounty",
 | 
					 | 
				
			||||||
                            //         "DeimosRuinsSacBounty"
 | 
					 | 
				
			||||||
                            //     ].some(ending => jobType.endsWith(ending))
 | 
					 | 
				
			||||||
                            // ) {
 | 
					 | 
				
			||||||
                            //     job.rewards = "TODO"; // Droptable for Arcana Isolation Vault
 | 
					 | 
				
			||||||
                            // }
 | 
					 | 
				
			||||||
                            if (
 | 
					                            if (
 | 
				
			||||||
                                [
 | 
					                                [
 | 
				
			||||||
                                    "DeimosEndlessAreaDefenseBounty",
 | 
					                                    "DeimosEndlessAreaDefenseBounty",
 | 
				
			||||||
@ -1657,10 +1763,10 @@ function getRandomMissionDrops(
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        if (
 | 
					                        if (
 | 
				
			||||||
                            RewardInfo.Q &&
 | 
					                            RewardInfo.Q &&
 | 
				
			||||||
                            (RewardInfo.JobStage === job.xpAmounts.length - 1 || job.isVault) &&
 | 
					                            (RewardInfo.JobStage === job.xpAmounts.length - 1 || jobType.endsWith("VaultBounty")) &&
 | 
				
			||||||
                            !isEndlessJob
 | 
					                            !isEndlessJob
 | 
				
			||||||
                        ) {
 | 
					                        ) {
 | 
				
			||||||
                            rotations.push(3);
 | 
					                            rotations.push(ExportRewards[job.rewards].length - 1);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -1954,3 +2060,24 @@ const getHexBounties = (seed: number): { nodes: string[]; buddies: string[] } =>
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return { nodes, buddies };
 | 
					    return { nodes, buddies };
 | 
				
			||||||
};*/
 | 
					};*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const goalMessagesByKey: Record<string, { sndr: string; msg: string; sub: string; icon: string }> = {
 | 
				
			||||||
 | 
					    "/Lotus/Types/Keys/GalleonRobberyAlert": {
 | 
				
			||||||
 | 
					        sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
 | 
				
			||||||
 | 
					        msg: "/Lotus/Language/Messages/GalleonRobbery2025RewardMsgA",
 | 
				
			||||||
 | 
					        sub: "/Lotus/Language/Messages/GalleonRobbery2025MissionTitleA",
 | 
				
			||||||
 | 
					        icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/Lotus/Types/Keys/GalleonRobberyAlertB": {
 | 
				
			||||||
 | 
					        sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
 | 
				
			||||||
 | 
					        msg: "/Lotus/Language/Messages/GalleonRobbery2025RewardMsgB",
 | 
				
			||||||
 | 
					        sub: "/Lotus/Language/Messages/GalleonRobbery2025MissionTitleB",
 | 
				
			||||||
 | 
					        icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/Lotus/Types/Keys/GalleonRobberyAlertC": {
 | 
				
			||||||
 | 
					        sndr: "/Lotus/Language/Bosses/BossCouncilorVayHek",
 | 
				
			||||||
 | 
					        msg: "/Lotus/Language/Messages/GalleonRobbery2025RewardMsgC",
 | 
				
			||||||
 | 
					        sub: "/Lotus/Language/Messages/GalleonRobbery2025MissionTitleC",
 | 
				
			||||||
 | 
					        icon: "/Lotus/Interface/Icons/Npcs/VayHekPortrait.png"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ import {
 | 
				
			|||||||
    updateCurrency,
 | 
					    updateCurrency,
 | 
				
			||||||
    updateSlots
 | 
					    updateSlots
 | 
				
			||||||
} from "@/src/services/inventoryService";
 | 
					} from "@/src/services/inventoryService";
 | 
				
			||||||
import { getRandomWeightedRewardUc } from "@/src/services/rngService";
 | 
					import { getRandomReward, getRandomWeightedRewardUc } from "@/src/services/rngService";
 | 
				
			||||||
import { applyStandingToVendorManifest, getVendorManifestByOid } from "@/src/services/serversideVendorsService";
 | 
					import { applyStandingToVendorManifest, getVendorManifestByOid } from "@/src/services/serversideVendorsService";
 | 
				
			||||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
					import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -16,10 +16,12 @@ import {
 | 
				
			|||||||
    IPurchaseResponse,
 | 
					    IPurchaseResponse,
 | 
				
			||||||
    SlotPurchase,
 | 
					    SlotPurchase,
 | 
				
			||||||
    IInventoryChanges,
 | 
					    IInventoryChanges,
 | 
				
			||||||
    PurchaseSource
 | 
					    PurchaseSource,
 | 
				
			||||||
 | 
					    IPurchaseParams
 | 
				
			||||||
} from "@/src/types/purchaseTypes";
 | 
					} from "@/src/types/purchaseTypes";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
import worldState from "@/static/fixed_responses/worldState/worldState.json";
 | 
					import { getWorldState } from "./worldStateService";
 | 
				
			||||||
 | 
					import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    ExportBoosterPacks,
 | 
					    ExportBoosterPacks,
 | 
				
			||||||
    ExportBoosters,
 | 
					    ExportBoosters,
 | 
				
			||||||
@ -34,6 +36,8 @@ import {
 | 
				
			|||||||
import { config } from "./configService";
 | 
					import { config } from "./configService";
 | 
				
			||||||
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
 | 
					import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
 | 
				
			||||||
import { fromStoreItem, toStoreItem } from "./itemDataService";
 | 
					import { fromStoreItem, toStoreItem } from "./itemDataService";
 | 
				
			||||||
 | 
					import { DailyDeal } from "../models/worldStateModel";
 | 
				
			||||||
 | 
					import { fromMongoDate, toMongoDate } from "../helpers/inventoryHelpers";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getStoreItemCategory = (storeItem: string): string => {
 | 
					export const getStoreItemCategory = (storeItem: string): string => {
 | 
				
			||||||
    const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
 | 
					    const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
 | 
				
			||||||
@ -50,6 +54,58 @@ export const getStoreItemTypesCategory = (typesItem: string): string => {
 | 
				
			|||||||
    return typeElements[1];
 | 
					    return typeElements[1];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const tallyVendorPurchase = (
 | 
				
			||||||
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
 | 
					    inventoryChanges: IInventoryChanges,
 | 
				
			||||||
 | 
					    VendorType: string,
 | 
				
			||||||
 | 
					    ItemId: string,
 | 
				
			||||||
 | 
					    numPurchased: number,
 | 
				
			||||||
 | 
					    Expiry: Date
 | 
				
			||||||
 | 
					): void => {
 | 
				
			||||||
 | 
					    if (!config.noVendorPurchaseLimits) {
 | 
				
			||||||
 | 
					        inventory.RecentVendorPurchases ??= [];
 | 
				
			||||||
 | 
					        let vendorPurchases = inventory.RecentVendorPurchases.find(x => x.VendorType == VendorType);
 | 
				
			||||||
 | 
					        if (!vendorPurchases) {
 | 
				
			||||||
 | 
					            vendorPurchases =
 | 
				
			||||||
 | 
					                inventory.RecentVendorPurchases[
 | 
				
			||||||
 | 
					                    inventory.RecentVendorPurchases.push({
 | 
				
			||||||
 | 
					                        VendorType: VendorType,
 | 
				
			||||||
 | 
					                        PurchaseHistory: []
 | 
				
			||||||
 | 
					                    }) - 1
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let historyEntry = vendorPurchases.PurchaseHistory.find(x => x.ItemId == ItemId);
 | 
				
			||||||
 | 
					        if (historyEntry) {
 | 
				
			||||||
 | 
					            if (Date.now() >= historyEntry.Expiry.getTime()) {
 | 
				
			||||||
 | 
					                historyEntry.NumPurchased = numPurchased;
 | 
				
			||||||
 | 
					                historyEntry.Expiry = Expiry;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                historyEntry.NumPurchased += numPurchased;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            historyEntry =
 | 
				
			||||||
 | 
					                vendorPurchases.PurchaseHistory[
 | 
				
			||||||
 | 
					                    vendorPurchases.PurchaseHistory.push({
 | 
				
			||||||
 | 
					                        ItemId: ItemId,
 | 
				
			||||||
 | 
					                        NumPurchased: numPurchased,
 | 
				
			||||||
 | 
					                        Expiry: Expiry
 | 
				
			||||||
 | 
					                    }) - 1
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        inventoryChanges.NewVendorPurchase = {
 | 
				
			||||||
 | 
					            VendorType: VendorType,
 | 
				
			||||||
 | 
					            PurchaseHistory: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ItemId: ItemId,
 | 
				
			||||||
 | 
					                    NumPurchased: historyEntry.NumPurchased,
 | 
				
			||||||
 | 
					                    Expiry: toMongoDate(Expiry)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        inventoryChanges.RecentVendorPurchases = inventoryChanges.NewVendorPurchase;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const handlePurchase = async (
 | 
					export const handlePurchase = async (
 | 
				
			||||||
    purchaseRequest: IPurchaseRequest,
 | 
					    purchaseRequest: IPurchaseRequest,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument
 | 
					    inventory: TInventoryDatabaseDocument
 | 
				
			||||||
@ -96,20 +152,7 @@ export const handlePurchase = async (
 | 
				
			|||||||
            if (offer.LocTagRandSeed !== undefined) {
 | 
					            if (offer.LocTagRandSeed !== undefined) {
 | 
				
			||||||
                seed = BigInt(offer.LocTagRandSeed);
 | 
					                seed = BigInt(offer.LocTagRandSeed);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (!config.noVendorPurchaseLimits && ItemId) {
 | 
					            if (ItemId) {
 | 
				
			||||||
                inventory.RecentVendorPurchases ??= [];
 | 
					 | 
				
			||||||
                let vendorPurchases = inventory.RecentVendorPurchases.find(
 | 
					 | 
				
			||||||
                    x => x.VendorType == manifest!.VendorInfo.TypeName
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                if (!vendorPurchases) {
 | 
					 | 
				
			||||||
                    vendorPurchases =
 | 
					 | 
				
			||||||
                        inventory.RecentVendorPurchases[
 | 
					 | 
				
			||||||
                            inventory.RecentVendorPurchases.push({
 | 
					 | 
				
			||||||
                                VendorType: manifest.VendorInfo.TypeName,
 | 
					 | 
				
			||||||
                                PurchaseHistory: []
 | 
					 | 
				
			||||||
                            }) - 1
 | 
					 | 
				
			||||||
                        ];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                let expiry = parseInt(offer.Expiry.$date.$numberLong);
 | 
					                let expiry = parseInt(offer.Expiry.$date.$numberLong);
 | 
				
			||||||
                if (purchaseRequest.PurchaseParams.IsWeekly) {
 | 
					                if (purchaseRequest.PurchaseParams.IsWeekly) {
 | 
				
			||||||
                    const EPOCH = 1734307200 * 1000; // Monday
 | 
					                    const EPOCH = 1734307200 * 1000; // Monday
 | 
				
			||||||
@ -117,34 +160,14 @@ export const handlePurchase = async (
 | 
				
			|||||||
                    const weekStart = EPOCH + week * 604800000;
 | 
					                    const weekStart = EPOCH + week * 604800000;
 | 
				
			||||||
                    expiry = weekStart + 604800000;
 | 
					                    expiry = weekStart + 604800000;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const historyEntry = vendorPurchases.PurchaseHistory.find(x => x.ItemId == ItemId);
 | 
					                tallyVendorPurchase(
 | 
				
			||||||
                let numPurchased = purchaseRequest.PurchaseParams.Quantity;
 | 
					                    inventory,
 | 
				
			||||||
                if (historyEntry) {
 | 
					                    prePurchaseInventoryChanges,
 | 
				
			||||||
                    if (Date.now() >= historyEntry.Expiry.getTime()) {
 | 
					                    manifest.VendorInfo.TypeName,
 | 
				
			||||||
                        historyEntry.NumPurchased = numPurchased;
 | 
					                    ItemId,
 | 
				
			||||||
                        historyEntry.Expiry = new Date(expiry);
 | 
					                    purchaseRequest.PurchaseParams.Quantity,
 | 
				
			||||||
                    } else {
 | 
					                    new Date(expiry)
 | 
				
			||||||
                        numPurchased += historyEntry.NumPurchased;
 | 
					                );
 | 
				
			||||||
                        historyEntry.NumPurchased += purchaseRequest.PurchaseParams.Quantity;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    vendorPurchases.PurchaseHistory.push({
 | 
					 | 
				
			||||||
                        ItemId: ItemId,
 | 
					 | 
				
			||||||
                        NumPurchased: purchaseRequest.PurchaseParams.Quantity,
 | 
					 | 
				
			||||||
                        Expiry: new Date(expiry)
 | 
					 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                prePurchaseInventoryChanges.NewVendorPurchase = {
 | 
					 | 
				
			||||||
                    VendorType: manifest.VendorInfo.TypeName,
 | 
					 | 
				
			||||||
                    PurchaseHistory: [
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            ItemId: ItemId,
 | 
					 | 
				
			||||||
                            NumPurchased: numPurchased,
 | 
					 | 
				
			||||||
                            Expiry: { $date: { $numberLong: expiry.toString() } }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    ]
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                prePurchaseInventoryChanges.RecentVendorPurchases = prePurchaseInventoryChanges.NewVendorPurchase;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier;
 | 
					            purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@ -175,6 +198,7 @@ export const handlePurchase = async (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    switch (purchaseRequest.PurchaseParams.Source) {
 | 
					    switch (purchaseRequest.PurchaseParams.Source) {
 | 
				
			||||||
        case PurchaseSource.VoidTrader: {
 | 
					        case PurchaseSource.VoidTrader: {
 | 
				
			||||||
 | 
					            const worldState = getWorldState();
 | 
				
			||||||
            if (purchaseRequest.PurchaseParams.SourceId! != worldState.VoidTraders[0]._id.$oid) {
 | 
					            if (purchaseRequest.PurchaseParams.SourceId! != worldState.VoidTraders[0]._id.$oid) {
 | 
				
			||||||
                throw new Error("invalid request source");
 | 
					                throw new Error("invalid request source");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -189,7 +213,7 @@ export const handlePurchase = async (
 | 
				
			|||||||
                    throw new Error(`vendor purchase should not have an expected price`);
 | 
					                    throw new Error(`vendor purchase should not have an expected price`);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (!config.dontSubtractPurchaseItemCost) {
 | 
					                if (offer.PrimePrice && !config.dontSubtractPurchaseItemCost) {
 | 
				
			||||||
                    const invItem: IMiscItem = {
 | 
					                    const invItem: IMiscItem = {
 | 
				
			||||||
                        ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
 | 
					                        ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
 | 
				
			||||||
                        ItemCount: offer.PrimePrice * purchaseRequest.PurchaseParams.Quantity * -1
 | 
					                        ItemCount: offer.PrimePrice * purchaseRequest.PurchaseParams.Quantity * -1
 | 
				
			||||||
@ -198,6 +222,17 @@ export const handlePurchase = async (
 | 
				
			|||||||
                    purchaseResponse.InventoryChanges.MiscItems ??= [];
 | 
					                    purchaseResponse.InventoryChanges.MiscItems ??= [];
 | 
				
			||||||
                    purchaseResponse.InventoryChanges.MiscItems.push(invItem);
 | 
					                    purchaseResponse.InventoryChanges.MiscItems.push(invItem);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (offer.Limit) {
 | 
				
			||||||
 | 
					                    tallyVendorPurchase(
 | 
				
			||||||
 | 
					                        inventory,
 | 
				
			||||||
 | 
					                        purchaseResponse.InventoryChanges,
 | 
				
			||||||
 | 
					                        "VoidTrader",
 | 
				
			||||||
 | 
					                        offer.ItemType,
 | 
				
			||||||
 | 
					                        purchaseRequest.PurchaseParams.Quantity,
 | 
				
			||||||
 | 
					                        fromMongoDate(worldState.VoidTraders[0].Expiry)
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -238,6 +273,12 @@ export const handlePurchase = async (
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
					        case PurchaseSource.DailyDeal:
 | 
				
			||||||
 | 
					            if (purchaseRequest.PurchaseParams.ExpectedPrice) {
 | 
				
			||||||
 | 
					                throw new Error(`daily deal purchase should not have an expected price`);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            await handleDailyDealPurchase(inventory, purchaseRequest.PurchaseParams, purchaseResponse);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
        case PurchaseSource.Vendor:
 | 
					        case PurchaseSource.Vendor:
 | 
				
			||||||
            if (purchaseRequest.PurchaseParams.SourceId! in ExportVendors) {
 | 
					            if (purchaseRequest.PurchaseParams.SourceId! in ExportVendors) {
 | 
				
			||||||
                const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
 | 
					                const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
 | 
				
			||||||
@ -264,14 +305,14 @@ export const handlePurchase = async (
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case PurchaseSource.PrimeVaultTrader: {
 | 
					        case PurchaseSource.PrimeVaultTrader: {
 | 
				
			||||||
            if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) {
 | 
					            if (purchaseRequest.PurchaseParams.SourceId! != staticWorldState.PrimeVaultTraders[0]._id.$oid) {
 | 
				
			||||||
                throw new Error("invalid request source");
 | 
					                throw new Error("invalid request source");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const offer =
 | 
					            const offer =
 | 
				
			||||||
                worldState.PrimeVaultTraders[0].Manifest.find(
 | 
					                staticWorldState.PrimeVaultTraders[0].Manifest.find(
 | 
				
			||||||
                    x => x.ItemType == purchaseRequest.PurchaseParams.StoreItem
 | 
					                    x => x.ItemType == purchaseRequest.PurchaseParams.StoreItem
 | 
				
			||||||
                ) ??
 | 
					                ) ??
 | 
				
			||||||
                worldState.PrimeVaultTraders[0].EvergreenManifest.find(
 | 
					                staticWorldState.PrimeVaultTraders[0].EvergreenManifest.find(
 | 
				
			||||||
                    x => x.ItemType == purchaseRequest.PurchaseParams.StoreItem
 | 
					                    x => x.ItemType == purchaseRequest.PurchaseParams.StoreItem
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            if (offer) {
 | 
					            if (offer) {
 | 
				
			||||||
@ -326,6 +367,25 @@ const handleItemPrices = (
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const handleDailyDealPurchase = async (
 | 
				
			||||||
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
 | 
					    purchaseParams: IPurchaseParams,
 | 
				
			||||||
 | 
					    purchaseResponse: IPurchaseResponse
 | 
				
			||||||
 | 
					): Promise<void> => {
 | 
				
			||||||
 | 
					    const dailyDeal = (await DailyDeal.findOne({ StoreItem: purchaseParams.StoreItem }))!;
 | 
				
			||||||
 | 
					    dailyDeal.AmountSold += 1;
 | 
				
			||||||
 | 
					    await dailyDeal.save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!config.dontSubtractPurchasePlatinumCost) {
 | 
				
			||||||
 | 
					        updateCurrency(inventory, dailyDeal.SalePrice, true, purchaseResponse.InventoryChanges);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!config.noVendorPurchaseLimits) {
 | 
				
			||||||
 | 
					        inventory.UsedDailyDeals.push(purchaseParams.StoreItem);
 | 
				
			||||||
 | 
					        purchaseResponse.DailyDealUsed = purchaseParams.StoreItem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const handleBundleAcqusition = async (
 | 
					export const handleBundleAcqusition = async (
 | 
				
			||||||
    storeItemName: string,
 | 
					    storeItemName: string,
 | 
				
			||||||
    inventory: TInventoryDatabaseDocument,
 | 
					    inventory: TInventoryDatabaseDocument,
 | 
				
			||||||
@ -369,18 +429,28 @@ export const handleStoreItemAcquisition = async (
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const storeCategory = getStoreItemCategory(storeItemName);
 | 
					        const storeCategory = getStoreItemCategory(storeItemName);
 | 
				
			||||||
        const internalName = fromStoreItem(storeItemName);
 | 
					        const internalName = fromStoreItem(storeItemName);
 | 
				
			||||||
        logger.debug(`store category ${storeCategory}`);
 | 
					 | 
				
			||||||
        if (!ignorePurchaseQuantity) {
 | 
					        if (!ignorePurchaseQuantity) {
 | 
				
			||||||
            if (internalName in ExportGear) {
 | 
					            if (internalName in ExportGear) {
 | 
				
			||||||
                quantity *= ExportGear[internalName].purchaseQuantity || 1;
 | 
					                quantity *= ExportGear[internalName].purchaseQuantity || 1;
 | 
				
			||||||
 | 
					                logger.debug(`factored quantity is ${quantity}`);
 | 
				
			||||||
            } else if (internalName in ExportResources) {
 | 
					            } else if (internalName in ExportResources) {
 | 
				
			||||||
                quantity *= ExportResources[internalName].purchaseQuantity || 1;
 | 
					                quantity *= ExportResources[internalName].purchaseQuantity || 1;
 | 
				
			||||||
 | 
					                logger.debug(`factored quantity is ${quantity}`);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        logger.debug(`store category ${storeCategory}`);
 | 
				
			||||||
        switch (storeCategory) {
 | 
					        switch (storeCategory) {
 | 
				
			||||||
            default: {
 | 
					            default: {
 | 
				
			||||||
                purchaseResponse = {
 | 
					                purchaseResponse = {
 | 
				
			||||||
                    InventoryChanges: await addItem(inventory, internalName, quantity, premiumPurchase, seed)
 | 
					                    InventoryChanges: await addItem(
 | 
				
			||||||
 | 
					                        inventory,
 | 
				
			||||||
 | 
					                        internalName,
 | 
				
			||||||
 | 
					                        quantity,
 | 
				
			||||||
 | 
					                        premiumPurchase,
 | 
				
			||||||
 | 
					                        seed,
 | 
				
			||||||
 | 
					                        undefined,
 | 
				
			||||||
 | 
					                        true
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -470,12 +540,57 @@ const handleBoosterPackPurchase = async (
 | 
				
			|||||||
            "attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server."
 | 
					            "attempt to roll over 100 booster packs in a single go. possible but unlikely to be desirable for the user or the server."
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    const specialItemReward = pack.components.find(x => x.PityIncreaseRate);
 | 
				
			||||||
    for (let i = 0; i != quantity; ++i) {
 | 
					    for (let i = 0; i != quantity; ++i) {
 | 
				
			||||||
        const disallowedItems = new Set();
 | 
					        if (specialItemReward) {
 | 
				
			||||||
        for (let roll = 0; roll != pack.rarityWeightsPerRoll.length; ) {
 | 
					            {
 | 
				
			||||||
            const weights = pack.rarityWeightsPerRoll[roll];
 | 
					                const normalComponents = [];
 | 
				
			||||||
            const result = getRandomWeightedRewardUc(pack.components, weights);
 | 
					                for (const comp of pack.components) {
 | 
				
			||||||
            if (result) {
 | 
					                    if (!comp.PityIncreaseRate) {
 | 
				
			||||||
 | 
					                        const { Probability, ...rest } = comp;
 | 
				
			||||||
 | 
					                        normalComponents.push({
 | 
				
			||||||
 | 
					                            ...rest,
 | 
				
			||||||
 | 
					                            probability: Probability!
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                const result = getRandomReward(normalComponents)!;
 | 
				
			||||||
 | 
					                logger.debug(`booster pack rolled`, result);
 | 
				
			||||||
 | 
					                purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
 | 
				
			||||||
 | 
					                combineInventoryChanges(
 | 
				
			||||||
 | 
					                    purchaseResponse.InventoryChanges,
 | 
				
			||||||
 | 
					                    await addItem(inventory, result.Item, result.Amount)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!inventory.WeaponSkins.some(x => x.ItemType == specialItemReward.Item)) {
 | 
				
			||||||
 | 
					                inventory.SpecialItemRewardAttenuation ??= [];
 | 
				
			||||||
 | 
					                let atten = inventory.SpecialItemRewardAttenuation.find(x => x.Tag == specialItemReward.Item);
 | 
				
			||||||
 | 
					                if (!atten) {
 | 
				
			||||||
 | 
					                    atten =
 | 
				
			||||||
 | 
					                        inventory.SpecialItemRewardAttenuation[
 | 
				
			||||||
 | 
					                            inventory.SpecialItemRewardAttenuation.push({
 | 
				
			||||||
 | 
					                                Tag: specialItemReward.Item,
 | 
				
			||||||
 | 
					                                Atten: specialItemReward.Probability!
 | 
				
			||||||
 | 
					                            }) - 1
 | 
				
			||||||
 | 
					                        ];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (Math.random() < atten.Atten) {
 | 
				
			||||||
 | 
					                    purchaseResponse.BoosterPackItems += toStoreItem(specialItemReward.Item) + ',{"lvl":0};';
 | 
				
			||||||
 | 
					                    combineInventoryChanges(
 | 
				
			||||||
 | 
					                        purchaseResponse.InventoryChanges,
 | 
				
			||||||
 | 
					                        await addItem(inventory, specialItemReward.Item)
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    // TOVERIFY: Is the SpecialItemRewardAttenuation entry removed now?
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    atten.Atten += specialItemReward.PityIncreaseRate!;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            const disallowedItems = new Set();
 | 
				
			||||||
 | 
					            for (let roll = 0; roll != pack.rarityWeightsPerRoll.length; ) {
 | 
				
			||||||
 | 
					                const weights = pack.rarityWeightsPerRoll[roll];
 | 
				
			||||||
 | 
					                const result = getRandomWeightedRewardUc(pack.components, weights)!;
 | 
				
			||||||
                logger.debug(`booster pack rolled`, result);
 | 
					                logger.debug(`booster pack rolled`, result);
 | 
				
			||||||
                if (disallowedItems.has(result.Item)) {
 | 
					                if (disallowedItems.has(result.Item)) {
 | 
				
			||||||
                    logger.debug(`oops, can't use that one; trying again`);
 | 
					                    logger.debug(`oops, can't use that one; trying again`);
 | 
				
			||||||
@ -485,9 +600,12 @@ const handleBoosterPackPurchase = async (
 | 
				
			|||||||
                    disallowedItems.add(result.Item);
 | 
					                    disallowedItems.add(result.Item);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
 | 
					                purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
 | 
				
			||||||
                combineInventoryChanges(purchaseResponse.InventoryChanges, await addItem(inventory, result.Item, 1));
 | 
					                combineInventoryChanges(
 | 
				
			||||||
 | 
					                    purchaseResponse.InventoryChanges,
 | 
				
			||||||
 | 
					                    await addItem(inventory, result.Item, result.Amount)
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                ++roll;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ++roll;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return purchaseResponse;
 | 
					    return purchaseResponse;
 | 
				
			||||||
@ -522,7 +640,9 @@ const handleTypesPurchase = async (
 | 
				
			|||||||
    logger.debug(`type category ${typeCategory}`);
 | 
					    logger.debug(`type category ${typeCategory}`);
 | 
				
			||||||
    switch (typeCategory) {
 | 
					    switch (typeCategory) {
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
            return { InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed) };
 | 
					            return {
 | 
				
			||||||
 | 
					                InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed, undefined, true)
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
        case "BoosterPacks":
 | 
					        case "BoosterPacks":
 | 
				
			||||||
            return handleBoosterPackPurchase(typesName, inventory, quantity);
 | 
					            return handleBoosterPackPurchase(typesName, inventory, quantity);
 | 
				
			||||||
        case "SlotItems":
 | 
					        case "SlotItems":
 | 
				
			||||||
 | 
				
			|||||||
@ -331,7 +331,7 @@ export const giveKeyChainMissionReward = async (
 | 
				
			|||||||
            const fixedLevelRewards = getLevelKeyRewards(missionName);
 | 
					            const fixedLevelRewards = getLevelKeyRewards(missionName);
 | 
				
			||||||
            if (fixedLevelRewards.levelKeyRewards) {
 | 
					            if (fixedLevelRewards.levelKeyRewards) {
 | 
				
			||||||
                const missionRewards: { StoreItem: string; ItemCount: number }[] = [];
 | 
					                const missionRewards: { StoreItem: string; ItemCount: number }[] = [];
 | 
				
			||||||
                addFixedLevelRewards(fixedLevelRewards.levelKeyRewards, inventory, missionRewards);
 | 
					                inventory.RegularCredits += addFixedLevelRewards(fixedLevelRewards.levelKeyRewards, missionRewards);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                for (const reward of missionRewards) {
 | 
					                for (const reward of missionRewards) {
 | 
				
			||||||
                    await addItem(inventory, fromStoreItem(reward.StoreItem), reward.ItemCount);
 | 
					                    await addItem(inventory, fromStoreItem(reward.StoreItem), reward.ItemCount);
 | 
				
			||||||
 | 
				
			|||||||
@ -107,6 +107,16 @@ export class SRng {
 | 
				
			|||||||
        return arr[this.randomInt(0, arr.length - 1)];
 | 
					        return arr[this.randomInt(0, arr.length - 1)];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    randomElementPop<T>(arr: T[]): T | undefined {
 | 
				
			||||||
 | 
					        if (arr.length != 0) {
 | 
				
			||||||
 | 
					            const index = this.randomInt(0, arr.length - 1);
 | 
				
			||||||
 | 
					            const elm = arr[index];
 | 
				
			||||||
 | 
					            arr.splice(index, 1);
 | 
				
			||||||
 | 
					            return elm;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return undefined;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    randomFloat(): number {
 | 
					    randomFloat(): number {
 | 
				
			||||||
        this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
 | 
					        this.state = (0x5851f42d4c957f2dn * this.state + 0x14057b7ef767814fn) & 0xffffffffffffffffn;
 | 
				
			||||||
        return (Number(this.state >> 38n) & 0xffffff) * 0.000000059604645;
 | 
					        return (Number(this.state >> 38n) & 0xffffff) * 0.000000059604645;
 | 
				
			||||||
 | 
				
			|||||||
@ -149,7 +149,8 @@ export const handleInventoryItemConfigChange = async (
 | 
				
			|||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        const inventoryItem = inventory.WeaponSkins.id(itemId);
 | 
					                        const inventoryItem = inventory.WeaponSkins.id(itemId);
 | 
				
			||||||
                        if (!inventoryItem) {
 | 
					                        if (!inventoryItem) {
 | 
				
			||||||
                            throw new Error(`inventory item WeaponSkins not found with id ${itemId}`);
 | 
					                            logger.warn(`inventory item WeaponSkins not found with id ${itemId}`);
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        if ("Favorite" in itemConfigEntries) {
 | 
					                        if ("Favorite" in itemConfigEntries) {
 | 
				
			||||||
                            inventoryItem.Favorite = itemConfigEntries.Favorite;
 | 
					                            inventoryItem.Favorite = itemConfigEntries.Favorite;
 | 
				
			||||||
@ -177,7 +178,8 @@ export const handleInventoryItemConfigChange = async (
 | 
				
			|||||||
                        const inventoryItem = inventory[equipmentName].id(itemId);
 | 
					                        const inventoryItem = inventory[equipmentName].id(itemId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (!inventoryItem) {
 | 
					                        if (!inventoryItem) {
 | 
				
			||||||
                            throw new Error(`inventory item ${equipmentName} not found with id ${itemId}`);
 | 
					                            logger.warn(`inventory item ${equipmentName} not found with id ${itemId}`);
 | 
				
			||||||
 | 
					                            continue;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        for (const [configId, config] of Object.entries(itemConfigEntries)) {
 | 
					                        for (const [configId, config] of Object.entries(itemConfigEntries)) {
 | 
				
			||||||
 | 
				
			|||||||
@ -281,6 +281,10 @@ const generateVendorManifest = (
 | 
				
			|||||||
                        offersToAdd.push(item);
 | 
					                        offersToAdd.push(item);
 | 
				
			||||||
                        ++offset;
 | 
					                        ++offset;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (missingItemsPerBin[item.bin]) {
 | 
				
			||||||
 | 
					                        missingItemsPerBin[item.bin] -= 1;
 | 
				
			||||||
 | 
					                        numOffersThatNeedToMatchABin -= 1;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    numCountedOffers += 1 + item.duplicates;
 | 
					                    numCountedOffers += 1 + item.duplicates;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -8,11 +8,12 @@ import {
 | 
				
			|||||||
} from "@/src/types/shipTypes";
 | 
					} from "@/src/types/shipTypes";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					import { logger } from "@/src/utils/logger";
 | 
				
			||||||
import { Types } from "mongoose";
 | 
					import { Types } from "mongoose";
 | 
				
			||||||
import { addShipDecorations, getInventory } from "./inventoryService";
 | 
					import { addFusionTreasures, addShipDecorations, getInventory } from "./inventoryService";
 | 
				
			||||||
import { config } from "./configService";
 | 
					import { config } from "./configService";
 | 
				
			||||||
import { Guild } from "../models/guildModel";
 | 
					import { Guild } from "../models/guildModel";
 | 
				
			||||||
import { hasGuildPermission } from "./guildService";
 | 
					import { hasGuildPermission } from "./guildService";
 | 
				
			||||||
import { GuildPermission } from "../types/guildTypes";
 | 
					import { GuildPermission } from "../types/guildTypes";
 | 
				
			||||||
 | 
					import { ExportResources } from "warframe-public-export-plus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const setShipCustomizations = async (
 | 
					export const setShipCustomizations = async (
 | 
				
			||||||
    accountId: string,
 | 
					    accountId: string,
 | 
				
			||||||
@ -58,7 +59,12 @@ export const handleSetShipDecorations = async (
 | 
				
			|||||||
    const roomToPlaceIn = rooms.find(room => room.Name === placedDecoration.Room);
 | 
					    const roomToPlaceIn = rooms.find(room => room.Name === placedDecoration.Room);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!roomToPlaceIn) {
 | 
					    if (!roomToPlaceIn) {
 | 
				
			||||||
        throw new Error("room not found");
 | 
					        throw new Error(`unknown room: ${placedDecoration.Room}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [itemType, meta] = Object.entries(ExportResources).find(arr => arr[1].deco == placedDecoration.Type)!;
 | 
				
			||||||
 | 
					    if (!itemType || meta.capacityCost === undefined) {
 | 
				
			||||||
 | 
					        throw new Error(`unknown deco type: ${placedDecoration.Type}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (placedDecoration.MoveId) {
 | 
					    if (placedDecoration.MoveId) {
 | 
				
			||||||
@ -82,7 +88,7 @@ export const handleSetShipDecorations = async (
 | 
				
			|||||||
                OldRoom: placedDecoration.OldRoom,
 | 
					                OldRoom: placedDecoration.OldRoom,
 | 
				
			||||||
                NewRoom: placedDecoration.Room,
 | 
					                NewRoom: placedDecoration.Room,
 | 
				
			||||||
                IsApartment: placedDecoration.IsApartment,
 | 
					                IsApartment: placedDecoration.IsApartment,
 | 
				
			||||||
                MaxCapacityIncrease: 0 // TODO: calculate capacity change upon removal
 | 
					                MaxCapacityIncrease: 0
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -95,33 +101,44 @@ export const handleSetShipDecorations = async (
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        oldRoom.PlacedDecos.pull({ _id: placedDecoration.MoveId });
 | 
					        oldRoom.PlacedDecos.pull({ _id: placedDecoration.MoveId });
 | 
				
			||||||
 | 
					        oldRoom.MaxCapacity += meta.capacityCost;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const newDecoration = {
 | 
					        const newDecoration = {
 | 
				
			||||||
            Type: placedDecoration.Type,
 | 
					            Type: placedDecoration.Type,
 | 
				
			||||||
            Pos: placedDecoration.Pos,
 | 
					            Pos: placedDecoration.Pos,
 | 
				
			||||||
            Rot: placedDecoration.Rot,
 | 
					            Rot: placedDecoration.Rot,
 | 
				
			||||||
            Scale: placedDecoration.Scale,
 | 
					            Scale: placedDecoration.Scale,
 | 
				
			||||||
 | 
					            Sockets: placedDecoration.Sockets,
 | 
				
			||||||
            _id: placedDecoration.MoveId
 | 
					            _id: placedDecoration.MoveId
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        //the new room is still roomToPlaceIn
 | 
					        //the new room is still roomToPlaceIn
 | 
				
			||||||
        roomToPlaceIn.PlacedDecos.push(newDecoration);
 | 
					        roomToPlaceIn.PlacedDecos.push(newDecoration);
 | 
				
			||||||
 | 
					        roomToPlaceIn.MaxCapacity -= meta.capacityCost;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await personalRooms.save();
 | 
					        await personalRooms.save();
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            OldRoom: placedDecoration.OldRoom,
 | 
					            OldRoom: placedDecoration.OldRoom,
 | 
				
			||||||
            NewRoom: placedDecoration.Room,
 | 
					            NewRoom: placedDecoration.Room,
 | 
				
			||||||
            IsApartment: placedDecoration.IsApartment,
 | 
					            IsApartment: placedDecoration.IsApartment,
 | 
				
			||||||
            MaxCapacityIncrease: 0 // TODO: calculate capacity change upon removal
 | 
					            MaxCapacityIncrease: -meta.capacityCost
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (placedDecoration.RemoveId) {
 | 
					    if (placedDecoration.RemoveId) {
 | 
				
			||||||
        roomToPlaceIn.PlacedDecos.pull({ _id: placedDecoration.RemoveId });
 | 
					        const decoIndex = roomToPlaceIn.PlacedDecos.findIndex(x => x._id.equals(placedDecoration.RemoveId));
 | 
				
			||||||
 | 
					        const deco = roomToPlaceIn.PlacedDecos[decoIndex];
 | 
				
			||||||
 | 
					        roomToPlaceIn.PlacedDecos.splice(decoIndex, 1);
 | 
				
			||||||
 | 
					        roomToPlaceIn.MaxCapacity += meta.capacityCost;
 | 
				
			||||||
        await personalRooms.save();
 | 
					        await personalRooms.save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!config.unlockAllShipDecorations) {
 | 
					        if (!config.unlockAllShipDecorations) {
 | 
				
			||||||
            const inventory = await getInventory(accountId);
 | 
					            const inventory = await getInventory(accountId);
 | 
				
			||||||
            addShipDecorations(inventory, [{ ItemType: placedDecoration.Type, ItemCount: 1 }]);
 | 
					            if (deco.Sockets !== undefined) {
 | 
				
			||||||
 | 
					                addFusionTreasures(inventory, [{ ItemType: itemType, Sockets: deco.Sockets, ItemCount: 1 }]);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                addShipDecorations(inventory, [{ ItemType: itemType, ItemCount: 1 }]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            await inventory.save();
 | 
					            await inventory.save();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -129,17 +146,20 @@ export const handleSetShipDecorations = async (
 | 
				
			|||||||
            DecoId: placedDecoration.RemoveId,
 | 
					            DecoId: placedDecoration.RemoveId,
 | 
				
			||||||
            Room: placedDecoration.Room,
 | 
					            Room: placedDecoration.Room,
 | 
				
			||||||
            IsApartment: placedDecoration.IsApartment,
 | 
					            IsApartment: placedDecoration.IsApartment,
 | 
				
			||||||
            MaxCapacityIncrease: 0
 | 
					            MaxCapacityIncrease: 0 // Client already implies the capacity being refunded.
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        if (!config.unlockAllShipDecorations) {
 | 
					 | 
				
			||||||
            const inventory = await getInventory(accountId);
 | 
					 | 
				
			||||||
            addShipDecorations(inventory, [{ ItemType: placedDecoration.Type, ItemCount: -1 }]);
 | 
					 | 
				
			||||||
            await inventory.save();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: handle capacity
 | 
					    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 {
 | 
				
			||||||
 | 
					            addShipDecorations(inventory, [{ ItemType: itemType, ItemCount: -1 }]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        await inventory.save();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //place decoration
 | 
					    //place decoration
 | 
				
			||||||
    const decoId = new Types.ObjectId();
 | 
					    const decoId = new Types.ObjectId();
 | 
				
			||||||
@ -148,12 +168,19 @@ export const handleSetShipDecorations = async (
 | 
				
			|||||||
        Pos: placedDecoration.Pos,
 | 
					        Pos: placedDecoration.Pos,
 | 
				
			||||||
        Rot: placedDecoration.Rot,
 | 
					        Rot: placedDecoration.Rot,
 | 
				
			||||||
        Scale: placedDecoration.Scale,
 | 
					        Scale: placedDecoration.Scale,
 | 
				
			||||||
 | 
					        Sockets: placedDecoration.Sockets,
 | 
				
			||||||
        _id: decoId
 | 
					        _id: decoId
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    roomToPlaceIn.MaxCapacity -= meta.capacityCost;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await personalRooms.save();
 | 
					    await personalRooms.save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return { DecoId: decoId.toString(), Room: placedDecoration.Room, IsApartment: placedDecoration.IsApartment };
 | 
					    return {
 | 
				
			||||||
 | 
					        DecoId: decoId.toString(),
 | 
				
			||||||
 | 
					        Room: placedDecoration.Room,
 | 
				
			||||||
 | 
					        IsApartment: placedDecoration.IsApartment,
 | 
				
			||||||
 | 
					        MaxCapacityIncrease: -meta.capacityCost
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlacedDecoInfoRequest): Promise<void> => {
 | 
					export const handleSetPlacedDecoInfo = async (accountId: string, req: ISetPlacedDecoInfoRequest): Promise<void> => {
 | 
				
			||||||
 | 
				
			|||||||
@ -136,7 +136,10 @@ export const stopWebServer = async (): Promise<void> => {
 | 
				
			|||||||
    await Promise.all(promises);
 | 
					    await Promise.all(promises);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let lastWsid: number = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IWsCustomData extends ws {
 | 
					interface IWsCustomData extends ws {
 | 
				
			||||||
 | 
					    id?: number;
 | 
				
			||||||
    accountId?: string;
 | 
					    accountId?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -150,6 +153,7 @@ interface IWsMsgFromClient {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IWsMsgToClient {
 | 
					interface IWsMsgToClient {
 | 
				
			||||||
 | 
					    //wsid?: number;
 | 
				
			||||||
    reload?: boolean;
 | 
					    reload?: boolean;
 | 
				
			||||||
    ports?: {
 | 
					    ports?: {
 | 
				
			||||||
        http: number | undefined;
 | 
					        http: number | undefined;
 | 
				
			||||||
@ -174,6 +178,10 @@ const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => {
 | 
				
			|||||||
        ws.close();
 | 
					        ws.close();
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    (ws as IWsCustomData).id = ++lastWsid;
 | 
				
			||||||
 | 
					    ws.send(JSON.stringify({ wsid: lastWsid }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // eslint-disable-next-line @typescript-eslint/no-misused-promises
 | 
					    // eslint-disable-next-line @typescript-eslint/no-misused-promises
 | 
				
			||||||
    ws.on("message", async msg => {
 | 
					    ws.on("message", async msg => {
 | 
				
			||||||
        const data = JSON.parse(String(msg)) as IWsMsgFromClient;
 | 
					        const data = JSON.parse(String(msg)) as IWsMsgFromClient;
 | 
				
			||||||
@ -268,3 +276,21 @@ export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const sendWsBroadcastExcept = (wsid: number | undefined, data: IWsMsgToClient): void => {
 | 
				
			||||||
 | 
					    const msg = JSON.stringify(data);
 | 
				
			||||||
 | 
					    if (wsServer) {
 | 
				
			||||||
 | 
					        for (const client of wsServer.clients) {
 | 
				
			||||||
 | 
					            if ((client as IWsCustomData).id != wsid) {
 | 
				
			||||||
 | 
					                client.send(msg);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (wssServer) {
 | 
				
			||||||
 | 
					        for (const client of wssServer.clients) {
 | 
				
			||||||
 | 
					            if ((client as IWsCustomData).id != wsid) {
 | 
				
			||||||
 | 
					                client.send(msg);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,10 @@
 | 
				
			|||||||
import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
 | 
					import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
 | 
				
			||||||
 | 
					import baro from "@/static/fixed_responses/worldState/baro.json";
 | 
				
			||||||
import fissureMissions from "@/static/fixed_responses/worldState/fissureMissions.json";
 | 
					import fissureMissions from "@/static/fixed_responses/worldState/fissureMissions.json";
 | 
				
			||||||
import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.json";
 | 
					import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.json";
 | 
				
			||||||
import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
 | 
					import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
 | 
				
			||||||
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
 | 
					import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
 | 
				
			||||||
 | 
					import darvoDeals from "@/static/fixed_responses/worldState/darvoDeals.json";
 | 
				
			||||||
import { buildConfig } from "@/src/services/buildConfigService";
 | 
					import { buildConfig } from "@/src/services/buildConfigService";
 | 
				
			||||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
					import { unixTimesInMs } from "@/src/constants/timeConstants";
 | 
				
			||||||
import { config } from "@/src/services/configService";
 | 
					import { config } from "@/src/services/configService";
 | 
				
			||||||
@ -19,12 +21,14 @@ import {
 | 
				
			|||||||
    ISyndicateMissionInfo,
 | 
					    ISyndicateMissionInfo,
 | 
				
			||||||
    ITmp,
 | 
					    ITmp,
 | 
				
			||||||
    IVoidStorm,
 | 
					    IVoidStorm,
 | 
				
			||||||
 | 
					    IVoidTrader,
 | 
				
			||||||
 | 
					    IVoidTraderOffer,
 | 
				
			||||||
    IWorldState,
 | 
					    IWorldState,
 | 
				
			||||||
    TCircuitGameMode
 | 
					    TCircuitGameMode
 | 
				
			||||||
} from "../types/worldStateTypes";
 | 
					} from "../types/worldStateTypes";
 | 
				
			||||||
import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers";
 | 
					import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers";
 | 
				
			||||||
import { logger } from "../utils/logger";
 | 
					import { logger } from "../utils/logger";
 | 
				
			||||||
import { Fissure } from "../models/worldStateModel";
 | 
					import { DailyDeal, Fissure } from "../models/worldStateModel";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sortieBosses = [
 | 
					const sortieBosses = [
 | 
				
			||||||
    "SORTIE_BOSS_HYENA",
 | 
					    "SORTIE_BOSS_HYENA",
 | 
				
			||||||
@ -98,7 +102,7 @@ const sortieBossNode: Record<Exclude<TSortieBoss, "SORTIE_BOSS_CORRUPTED_VOR">,
 | 
				
			|||||||
    SORTIE_BOSS_VOR: "SolNode108"
 | 
					    SORTIE_BOSS_VOR: "SolNode108"
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const eidolonJobs = [
 | 
					const eidolonJobs: readonly string[] = [
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/AssassinateBountyAss",
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/AssassinateBountyAss",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/AssassinateBountyCap",
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/AssassinateBountyCap",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/AttritionBountySab",
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/AttritionBountySab",
 | 
				
			||||||
@ -114,14 +118,14 @@ const eidolonJobs = [
 | 
				
			|||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/RescueBountyResc"
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/RescueBountyResc"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const eidolonNarmerJobs = [
 | 
					const eidolonNarmerJobs: readonly string[] = [
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AssassinateBountyAss",
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AssassinateBountyAss",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AttritionBountyExt",
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AttritionBountyExt",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/ReclamationBountyTheft",
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/ReclamationBountyTheft",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AttritionBountyLib"
 | 
					    "/Lotus/Types/Gameplay/Eidolon/Jobs/Narmer/AttritionBountyLib"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const venusJobs = [
 | 
					const venusJobs: readonly string[] = [
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobAmbush",
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobAmbush",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobExcavation",
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobExcavation",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobRecovery",
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/VenusArtifactJobRecovery",
 | 
				
			||||||
@ -147,14 +151,14 @@ const venusJobs = [
 | 
				
			|||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/VenusWetworkJobSpy"
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/VenusWetworkJobSpy"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const venusNarmerJobs = [
 | 
					const venusNarmerJobs: readonly string[] = [
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusCullJobAssassinate",
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusCullJobAssassinate",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusCullJobExterminate",
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusCullJobExterminate",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusPreservationJobDefense",
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusPreservationJobDefense",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusTheftJobExcavation"
 | 
					    "/Lotus/Types/Gameplay/Venus/Jobs/Narmer/NarmerVenusTheftJobExcavation"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const microplanetJobs = [
 | 
					const microplanetJobs: readonly string[] = [
 | 
				
			||||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosAreaDefenseBounty",
 | 
					    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosAreaDefenseBounty",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosAssassinateBounty",
 | 
					    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosAssassinateBounty",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosCrpSurvivorBounty",
 | 
					    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosCrpSurvivorBounty",
 | 
				
			||||||
@ -164,7 +168,7 @@ const microplanetJobs = [
 | 
				
			|||||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosPurifyBounty"
 | 
					    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosPurifyBounty"
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const microplanetEndlessJobs = [
 | 
					const microplanetEndlessJobs: readonly string[] = [
 | 
				
			||||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessAreaDefenseBounty",
 | 
					    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessAreaDefenseBounty",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessExcavateBounty",
 | 
					    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessExcavateBounty",
 | 
				
			||||||
    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessPurifyBounty"
 | 
					    "/Lotus/Types/Gameplay/InfestedMicroplanet/Jobs/DeimosEndlessPurifyBounty"
 | 
				
			||||||
@ -369,7 +373,7 @@ const getSeasonChallengePools = (syndicateTag: string): IRotatingSeasonChallenge
 | 
				
			|||||||
        hardWeekly: syndicate.weeklyChallenges!.filter(x =>
 | 
					        hardWeekly: syndicate.weeklyChallenges!.filter(x =>
 | 
				
			||||||
            x.startsWith("/Lotus/Types/Challenges/Seasons/WeeklyHard/")
 | 
					            x.startsWith("/Lotus/Types/Challenges/Seasons/WeeklyHard/")
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        hasWeeklyPermanent: !!syndicate.weeklyChallenges!.find(x =>
 | 
					        hasWeeklyPermanent: syndicate.weeklyChallenges!.some(x =>
 | 
				
			||||||
            x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")
 | 
					            x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@ -495,6 +499,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        const rng = new SRng(seed);
 | 
					        const rng = new SRng(seed);
 | 
				
			||||||
 | 
					        const pool = [...eidolonJobs];
 | 
				
			||||||
        syndicateMissions.push({
 | 
					        syndicateMissions.push({
 | 
				
			||||||
            _id: {
 | 
					            _id: {
 | 
				
			||||||
                $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000008"
 | 
					                $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000008"
 | 
				
			||||||
@ -506,7 +511,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
            Nodes: [],
 | 
					            Nodes: [],
 | 
				
			||||||
            Jobs: [
 | 
					            Jobs: [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(eidolonJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierATable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 0,
 | 
					                    masteryReq: 0,
 | 
				
			||||||
                    minEnemyLevel: 5,
 | 
					                    minEnemyLevel: 5,
 | 
				
			||||||
@ -514,7 +519,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 3, 1000, 1500)
 | 
					                    xpAmounts: generateXpAmounts(rng, 3, 1000, 1500)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(eidolonJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierBTable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierBTable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 1,
 | 
					                    masteryReq: 1,
 | 
				
			||||||
                    minEnemyLevel: 10,
 | 
					                    minEnemyLevel: 10,
 | 
				
			||||||
@ -522,7 +527,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 3, 1750, 2250)
 | 
					                    xpAmounts: generateXpAmounts(rng, 3, 1750, 2250)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(eidolonJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierCTable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierCTable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 2,
 | 
					                    masteryReq: 2,
 | 
				
			||||||
                    minEnemyLevel: 20,
 | 
					                    minEnemyLevel: 20,
 | 
				
			||||||
@ -530,7 +535,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 4, 2500, 3000)
 | 
					                    xpAmounts: generateXpAmounts(rng, 4, 2500, 3000)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(eidolonJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierDTable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierDTable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 3,
 | 
					                    masteryReq: 3,
 | 
				
			||||||
                    minEnemyLevel: 30,
 | 
					                    minEnemyLevel: 30,
 | 
				
			||||||
@ -538,7 +543,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 5, 3250, 3750)
 | 
					                    xpAmounts: generateXpAmounts(rng, 5, 3250, 3750)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(eidolonJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 5,
 | 
					                    masteryReq: 5,
 | 
				
			||||||
                    minEnemyLevel: 40,
 | 
					                    minEnemyLevel: 40,
 | 
				
			||||||
@ -546,7 +551,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 5, 4000, 4500)
 | 
					                    xpAmounts: generateXpAmounts(rng, 5, 4000, 4500)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(eidolonJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/EidolonJobMissionRewards/TierETable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 10,
 | 
					                    masteryReq: 10,
 | 
				
			||||||
                    minEnemyLevel: 100,
 | 
					                    minEnemyLevel: 100,
 | 
				
			||||||
@ -567,6 +572,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        const rng = new SRng(seed);
 | 
					        const rng = new SRng(seed);
 | 
				
			||||||
 | 
					        const pool = [...venusJobs];
 | 
				
			||||||
        syndicateMissions.push({
 | 
					        syndicateMissions.push({
 | 
				
			||||||
            _id: {
 | 
					            _id: {
 | 
				
			||||||
                $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000025"
 | 
					                $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000025"
 | 
				
			||||||
@ -578,7 +584,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
            Nodes: [],
 | 
					            Nodes: [],
 | 
				
			||||||
            Jobs: [
 | 
					            Jobs: [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(venusJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierATable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierATable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 0,
 | 
					                    masteryReq: 0,
 | 
				
			||||||
                    minEnemyLevel: 5,
 | 
					                    minEnemyLevel: 5,
 | 
				
			||||||
@ -586,7 +592,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 3, 1000, 1500)
 | 
					                    xpAmounts: generateXpAmounts(rng, 3, 1000, 1500)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(venusJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierBTable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierBTable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 1,
 | 
					                    masteryReq: 1,
 | 
				
			||||||
                    minEnemyLevel: 10,
 | 
					                    minEnemyLevel: 10,
 | 
				
			||||||
@ -594,7 +600,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 3, 1750, 2250)
 | 
					                    xpAmounts: generateXpAmounts(rng, 3, 1750, 2250)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(venusJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierCTable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierCTable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 2,
 | 
					                    masteryReq: 2,
 | 
				
			||||||
                    minEnemyLevel: 20,
 | 
					                    minEnemyLevel: 20,
 | 
				
			||||||
@ -602,7 +608,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 4, 2500, 3000)
 | 
					                    xpAmounts: generateXpAmounts(rng, 4, 2500, 3000)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(venusJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierDTable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierDTable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 3,
 | 
					                    masteryReq: 3,
 | 
				
			||||||
                    minEnemyLevel: 30,
 | 
					                    minEnemyLevel: 30,
 | 
				
			||||||
@ -610,7 +616,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 5, 3250, 3750)
 | 
					                    xpAmounts: generateXpAmounts(rng, 5, 3250, 3750)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(venusJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 5,
 | 
					                    masteryReq: 5,
 | 
				
			||||||
                    minEnemyLevel: 40,
 | 
					                    minEnemyLevel: 40,
 | 
				
			||||||
@ -618,7 +624,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 5, 4000, 4500)
 | 
					                    xpAmounts: generateXpAmounts(rng, 5, 4000, 4500)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(venusJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/VenusJobMissionRewards/VenusTierETable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 10,
 | 
					                    masteryReq: 10,
 | 
				
			||||||
                    minEnemyLevel: 100,
 | 
					                    minEnemyLevel: 100,
 | 
				
			||||||
@ -639,6 +645,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        const rng = new SRng(seed);
 | 
					        const rng = new SRng(seed);
 | 
				
			||||||
 | 
					        const pool = [...microplanetJobs];
 | 
				
			||||||
        syndicateMissions.push({
 | 
					        syndicateMissions.push({
 | 
				
			||||||
            _id: {
 | 
					            _id: {
 | 
				
			||||||
                $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000002"
 | 
					                $oid: ((bountyCycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "0000000000000002"
 | 
				
			||||||
@ -650,7 +657,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
            Nodes: [],
 | 
					            Nodes: [],
 | 
				
			||||||
            Jobs: [
 | 
					            Jobs: [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(microplanetJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierATable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierATable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 0,
 | 
					                    masteryReq: 0,
 | 
				
			||||||
                    minEnemyLevel: 5,
 | 
					                    minEnemyLevel: 5,
 | 
				
			||||||
@ -658,7 +665,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 3, 12, 18)
 | 
					                    xpAmounts: generateXpAmounts(rng, 3, 12, 18)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(microplanetJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierCTable${table}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierCTable${table}Rewards`,
 | 
				
			||||||
                    masteryReq: 1,
 | 
					                    masteryReq: 1,
 | 
				
			||||||
                    minEnemyLevel: 15,
 | 
					                    minEnemyLevel: 15,
 | 
				
			||||||
@ -675,7 +682,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: [14, 14, 14]
 | 
					                    xpAmounts: [14, 14, 14]
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(microplanetJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierDTable${deimosDTable}Rewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierDTable${deimosDTable}Rewards`,
 | 
				
			||||||
                    masteryReq: 2,
 | 
					                    masteryReq: 2,
 | 
				
			||||||
                    minEnemyLevel: 30,
 | 
					                    minEnemyLevel: 30,
 | 
				
			||||||
@ -683,7 +690,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 4, 72, 88)
 | 
					                    xpAmounts: generateXpAmounts(rng, 4, 72, 88)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(microplanetJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards`,
 | 
				
			||||||
                    masteryReq: 3,
 | 
					                    masteryReq: 3,
 | 
				
			||||||
                    minEnemyLevel: 40,
 | 
					                    minEnemyLevel: 40,
 | 
				
			||||||
@ -691,7 +698,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
 | 
				
			|||||||
                    xpAmounts: generateXpAmounts(rng, 5, 115, 135)
 | 
					                    xpAmounts: generateXpAmounts(rng, 5, 115, 135)
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    jobType: rng.randomElement(microplanetJobs),
 | 
					                    jobType: rng.randomElementPop(pool),
 | 
				
			||||||
                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards`,
 | 
					                    rewards: `/Lotus/Types/Game/MissionDecks/DeimosMissionRewards/TierETableARewards`,
 | 
				
			||||||
                    masteryReq: 10,
 | 
					                    masteryReq: 10,
 | 
				
			||||||
                    minEnemyLevel: 100,
 | 
					                    minEnemyLevel: 100,
 | 
				
			||||||
@ -1114,7 +1121,9 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        LiteSorties: [],
 | 
					        LiteSorties: [],
 | 
				
			||||||
        ActiveMissions: [],
 | 
					        ActiveMissions: [],
 | 
				
			||||||
        GlobalUpgrades: [],
 | 
					        GlobalUpgrades: [],
 | 
				
			||||||
 | 
					        VoidTraders: [],
 | 
				
			||||||
        VoidStorms: [],
 | 
					        VoidStorms: [],
 | 
				
			||||||
 | 
					        DailyDeals: [],
 | 
				
			||||||
        EndlessXpChoices: [],
 | 
					        EndlessXpChoices: [],
 | 
				
			||||||
        KnownCalendarSeasons: [],
 | 
					        KnownCalendarSeasons: [],
 | 
				
			||||||
        ...staticWorldState,
 | 
					        ...staticWorldState,
 | 
				
			||||||
@ -1142,6 +1151,77 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
            Node: "SolarisUnitedHub1"
 | 
					            Node: "SolarisUnitedHub1"
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    // The client gets kinda confused when multiple goals have the same tag, so considering these mutually exclusive.
 | 
				
			||||||
 | 
					    if (config.worldState?.galleonOfGhouls == 1) {
 | 
				
			||||||
 | 
					        worldState.Goals.push({
 | 
				
			||||||
 | 
					            _id: { $oid: "6814ddf00000000000000000" },
 | 
				
			||||||
 | 
					            Activation: { $date: { $numberLong: "1746198000000" } },
 | 
				
			||||||
 | 
					            Expiry: { $date: { $numberLong: "2000000000000" } },
 | 
				
			||||||
 | 
					            Count: 0,
 | 
				
			||||||
 | 
					            Goal: 1,
 | 
				
			||||||
 | 
					            Success: 0,
 | 
				
			||||||
 | 
					            Personal: true,
 | 
				
			||||||
 | 
					            Bounty: true,
 | 
				
			||||||
 | 
					            ClampNodeScores: true,
 | 
				
			||||||
 | 
					            Node: "EventNode19",
 | 
				
			||||||
 | 
					            MissionKeyName: "/Lotus/Types/Keys/GalleonRobberyAlert",
 | 
				
			||||||
 | 
					            Desc: "/Lotus/Language/Events/GalleonRobberyEventMissionTitle",
 | 
				
			||||||
 | 
					            Icon: "/Lotus/Interface/Icons/Player/GalleonRobberiesEvent.png",
 | 
				
			||||||
 | 
					            Tag: "GalleonRobbery",
 | 
				
			||||||
 | 
					            Reward: {
 | 
				
			||||||
 | 
					                items: [
 | 
				
			||||||
 | 
					                    "/Lotus/StoreItems/Types/Recipes/Weapons/GrnChainSawTonfaBlueprint",
 | 
				
			||||||
 | 
					                    "/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    } else if (config.worldState?.galleonOfGhouls == 2) {
 | 
				
			||||||
 | 
					        worldState.Goals.push({
 | 
				
			||||||
 | 
					            _id: { $oid: "681e18700000000000000000" },
 | 
				
			||||||
 | 
					            Activation: { $date: { $numberLong: "1746802800000" } },
 | 
				
			||||||
 | 
					            Expiry: { $date: { $numberLong: "2000000000000" } },
 | 
				
			||||||
 | 
					            Count: 0,
 | 
				
			||||||
 | 
					            Goal: 1,
 | 
				
			||||||
 | 
					            Success: 0,
 | 
				
			||||||
 | 
					            Personal: true,
 | 
				
			||||||
 | 
					            Bounty: true,
 | 
				
			||||||
 | 
					            ClampNodeScores: true,
 | 
				
			||||||
 | 
					            Node: "EventNode28",
 | 
				
			||||||
 | 
					            MissionKeyName: "/Lotus/Types/Keys/GalleonRobberyAlertB",
 | 
				
			||||||
 | 
					            Desc: "/Lotus/Language/Events/GalleonRobberyEventMissionTitle",
 | 
				
			||||||
 | 
					            Icon: "/Lotus/Interface/Icons/Player/GalleonRobberiesEvent.png",
 | 
				
			||||||
 | 
					            Tag: "GalleonRobbery",
 | 
				
			||||||
 | 
					            Reward: {
 | 
				
			||||||
 | 
					                items: [
 | 
				
			||||||
 | 
					                    "/Lotus/StoreItems/Types/Recipes/Weapons/MortiforShieldAndSwordBlueprint",
 | 
				
			||||||
 | 
					                    "/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    } else if (config.worldState?.galleonOfGhouls == 3) {
 | 
				
			||||||
 | 
					        worldState.Goals.push({
 | 
				
			||||||
 | 
					            _id: { $oid: "682752f00000000000000000" },
 | 
				
			||||||
 | 
					            Activation: { $date: { $numberLong: "1747407600000" } },
 | 
				
			||||||
 | 
					            Expiry: { $date: { $numberLong: "2000000000000" } },
 | 
				
			||||||
 | 
					            Count: 0,
 | 
				
			||||||
 | 
					            Goal: 1,
 | 
				
			||||||
 | 
					            Success: 0,
 | 
				
			||||||
 | 
					            Personal: true,
 | 
				
			||||||
 | 
					            Bounty: true,
 | 
				
			||||||
 | 
					            ClampNodeScores: true,
 | 
				
			||||||
 | 
					            Node: "EventNode19",
 | 
				
			||||||
 | 
					            MissionKeyName: "/Lotus/Types/Keys/GalleonRobberyAlertC",
 | 
				
			||||||
 | 
					            Desc: "/Lotus/Language/Events/GalleonRobberyEventMissionTitle",
 | 
				
			||||||
 | 
					            Icon: "/Lotus/Interface/Icons/Player/GalleonRobberiesEvent.png",
 | 
				
			||||||
 | 
					            Tag: "GalleonRobbery",
 | 
				
			||||||
 | 
					            Reward: {
 | 
				
			||||||
 | 
					                items: [
 | 
				
			||||||
 | 
					                    "/Lotus/Types/StoreItems/Packages/EventCatalystReactorBundle",
 | 
				
			||||||
 | 
					                    "/Lotus/StoreItems/Upgrades/Skins/Clan/BountyHunterBadgeItem"
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Nightwave Challenges
 | 
					    // Nightwave Challenges
 | 
				
			||||||
    const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
 | 
					    const nightwaveSyndicateTag = getNightwaveSyndicateTag(buildLabel);
 | 
				
			||||||
@ -1242,6 +1322,77 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Baro
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        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);
 | 
				
			||||||
 | 
					        const baroEnd = baroStart + unixTimesInMs.day * 14;
 | 
				
			||||||
 | 
					        const baroNode = ["EarthHUB", "MercuryHUB", "SaturnHUB", "PlutoHUB"][baroIndex % 4];
 | 
				
			||||||
 | 
					        const vt: IVoidTrader = {
 | 
				
			||||||
 | 
					            _id: { $oid: ((baroStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "493c96d6067610bc" },
 | 
				
			||||||
 | 
					            Activation: { $date: { $numberLong: baroActualStart.toString() } },
 | 
				
			||||||
 | 
					            Expiry: { $date: { $numberLong: baroEnd.toString() } },
 | 
				
			||||||
 | 
					            Character: "Baro'Ki Teel",
 | 
				
			||||||
 | 
					            Node: baroNode,
 | 
				
			||||||
 | 
					            Manifest: []
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        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);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                const rng = new SRng(new SRng(baroIndex).randomInt(0, 100_000));
 | 
				
			||||||
 | 
					                // TOVERIFY: Constraint for upgrades amount?
 | 
				
			||||||
 | 
					                // TOVERIFY: Constraint for weapon amount?
 | 
				
			||||||
 | 
					                // TOVERIFY: Constraint for relics amount?
 | 
				
			||||||
 | 
					                let armorSet = rng.randomElement(baro.armorSets)!;
 | 
				
			||||||
 | 
					                if (Array.isArray(armorSet[0])) {
 | 
				
			||||||
 | 
					                    armorSet = rng.randomElement(baro.armorSets)!;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                while (vt.Manifest.length + armorSet.length < 31) {
 | 
				
			||||||
 | 
					                    const item = rng.randomElement(baro.rest)!;
 | 
				
			||||||
 | 
					                    if (vt.Manifest.indexOf(item) == -1) {
 | 
				
			||||||
 | 
					                        const set = baro.allIfAny.find(set => set.indexOf(item.ItemType) != -1);
 | 
				
			||||||
 | 
					                        if (set) {
 | 
				
			||||||
 | 
					                            for (const itemType of set) {
 | 
				
			||||||
 | 
					                                vt.Manifest.push(baro.rest.find(x => x.ItemType == itemType)!);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            vt.Manifest.push(item);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                const overflow = 31 - (vt.Manifest.length + armorSet.length);
 | 
				
			||||||
 | 
					                if (overflow > 0) {
 | 
				
			||||||
 | 
					                    vt.Manifest.splice(0, overflow);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                for (const armor of armorSet) {
 | 
				
			||||||
 | 
					                    vt.Manifest.push(armor as IVoidTraderOffer);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for (const item of baro.evergreen) {
 | 
				
			||||||
 | 
					                vt.Manifest.push(item);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
 | 
					    // Sortie & syndicate missions cycling every day (at 16:00 or 17:00 UTC depending on if London, OT is observing DST)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        const rollover = getSortieTime(day);
 | 
					        const rollover = getSortieTime(day);
 | 
				
			||||||
@ -1375,20 +1526,58 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const populateFissures = async (worldState: IWorldState): Promise<void> => {
 | 
					export const populateFissures = async (worldState: IWorldState): Promise<void> => {
 | 
				
			||||||
    const fissures = await Fissure.find({});
 | 
					    if (config.worldState?.allTheFissures) {
 | 
				
			||||||
    for (const fissure of fissures) {
 | 
					        let i = 0;
 | 
				
			||||||
        const meta = ExportRegions[fissure.Node];
 | 
					        for (const [tier, nodes] of Object.entries(fissureMissions)) {
 | 
				
			||||||
        worldState.ActiveMissions.push({
 | 
					            for (const node of nodes) {
 | 
				
			||||||
            _id: toOid(fissure._id),
 | 
					                const meta = ExportRegions[node];
 | 
				
			||||||
            Region: meta.systemIndex + 1,
 | 
					                worldState.ActiveMissions.push({
 | 
				
			||||||
            Seed: 1337,
 | 
					                    _id: { $oid: (i++).toString().padStart(8, "0") + "8e0c70ba050f1eb7" },
 | 
				
			||||||
            Activation: toMongoDate(fissure.Activation),
 | 
					                    Region: meta.systemIndex + 1,
 | 
				
			||||||
            Expiry: toMongoDate(fissure.Expiry),
 | 
					                    Seed: 1337,
 | 
				
			||||||
            Node: fissure.Node,
 | 
					                    Activation: { $date: { $numberLong: "1000000000000" } },
 | 
				
			||||||
            MissionType: eMissionType[meta.missionIndex].tag,
 | 
					                    Expiry: { $date: { $numberLong: "2000000000000" } },
 | 
				
			||||||
            Modifier: fissure.Modifier,
 | 
					                    Node: node,
 | 
				
			||||||
            Hard: fissure.Hard
 | 
					                    MissionType: eMissionType[meta.missionIndex].tag,
 | 
				
			||||||
        });
 | 
					                    Modifier: tier,
 | 
				
			||||||
 | 
					                    Hard: config.worldState.allTheFissures == "hard"
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        const fissures = await Fissure.find({});
 | 
				
			||||||
 | 
					        for (const fissure of fissures) {
 | 
				
			||||||
 | 
					            const meta = ExportRegions[fissure.Node];
 | 
				
			||||||
 | 
					            worldState.ActiveMissions.push({
 | 
				
			||||||
 | 
					                _id: toOid(fissure._id),
 | 
				
			||||||
 | 
					                Region: meta.systemIndex + 1,
 | 
				
			||||||
 | 
					                Seed: 1337,
 | 
				
			||||||
 | 
					                Activation: toMongoDate(fissure.Activation),
 | 
				
			||||||
 | 
					                Expiry: toMongoDate(fissure.Expiry),
 | 
				
			||||||
 | 
					                Node: fissure.Node,
 | 
				
			||||||
 | 
					                MissionType: eMissionType[meta.missionIndex].tag,
 | 
				
			||||||
 | 
					                Modifier: fissure.Modifier,
 | 
				
			||||||
 | 
					                Hard: fissure.Hard
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const populateDailyDeal = async (worldState: IWorldState): Promise<void> => {
 | 
				
			||||||
 | 
					    const dailyDeals = await DailyDeal.find({});
 | 
				
			||||||
 | 
					    for (const dailyDeal of dailyDeals) {
 | 
				
			||||||
 | 
					        if (dailyDeal.Expiry.getTime() > Date.now()) {
 | 
				
			||||||
 | 
					            worldState.DailyDeals.push({
 | 
				
			||||||
 | 
					                StoreItem: dailyDeal.StoreItem,
 | 
				
			||||||
 | 
					                Activation: toMongoDate(dailyDeal.Activation),
 | 
				
			||||||
 | 
					                Expiry: toMongoDate(dailyDeal.Expiry),
 | 
				
			||||||
 | 
					                Discount: dailyDeal.Discount,
 | 
				
			||||||
 | 
					                OriginalPrice: dailyDeal.OriginalPrice,
 | 
				
			||||||
 | 
					                SalePrice: dailyDeal.SalePrice,
 | 
				
			||||||
 | 
					                AmountTotal: Math.round(dailyDeal.AmountTotal * (config.worldState?.darvoStockMultiplier ?? 1)),
 | 
				
			||||||
 | 
					                AmountSold: dailyDeal.AmountSold
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1520,7 +1709,7 @@ const nightwaveTagToSeason: Record<string, number> = {
 | 
				
			|||||||
    RadioLegionSyndicate: 0 // The Wolf of Saturn Six
 | 
					    RadioLegionSyndicate: 0 // The Wolf of Saturn Six
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const updateWorldStateCollections = async (): Promise<void> => {
 | 
					const updateFissures = async (): Promise<void> => {
 | 
				
			||||||
    const fissures = await Fissure.find();
 | 
					    const fissures = await Fissure.find();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const activeNodes = new Set<string>();
 | 
					    const activeNodes = new Set<string>();
 | 
				
			||||||
@ -1573,3 +1762,38 @@ export const updateWorldStateCollections = async (): Promise<void> => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const updateDailyDeal = async (): Promise<void> => {
 | 
				
			||||||
 | 
					    let darvoIndex = Math.trunc((Date.now() - 25200000) / (26 * unixTimesInMs.hour));
 | 
				
			||||||
 | 
					    let darvoEnd;
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        const darvoStart = darvoIndex * (26 * unixTimesInMs.hour) + 25200000;
 | 
				
			||||||
 | 
					        darvoEnd = darvoStart + 26 * unixTimesInMs.hour;
 | 
				
			||||||
 | 
					        const darvoOid = ((darvoStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") + "adc51a72f7324d95";
 | 
				
			||||||
 | 
					        if (!(await DailyDeal.findById(darvoOid))) {
 | 
				
			||||||
 | 
					            const seed = new SRng(darvoIndex).randomInt(0, 100_000);
 | 
				
			||||||
 | 
					            const rng = new SRng(seed);
 | 
				
			||||||
 | 
					            let deal;
 | 
				
			||||||
 | 
					            do {
 | 
				
			||||||
 | 
					                deal = rng.randomReward(darvoDeals)!; // Using an actual sampling collected over roughly a year because I can't extrapolate an algorithm from it with enough certainty.
 | 
				
			||||||
 | 
					                //const [storeItem, meta] = rng.randomElement(Object.entries(darvoDeals))!;
 | 
				
			||||||
 | 
					                //const discount = Math.min(rng.randomInt(1, 9) * 10, (meta as { MaxDiscount?: number }).MaxDiscount ?? 1);
 | 
				
			||||||
 | 
					            } while (await DailyDeal.exists({ StoreItem: deal.StoreItem }));
 | 
				
			||||||
 | 
					            await DailyDeal.insertOne({
 | 
				
			||||||
 | 
					                _id: darvoOid,
 | 
				
			||||||
 | 
					                StoreItem: deal.StoreItem,
 | 
				
			||||||
 | 
					                Activation: new Date(darvoStart),
 | 
				
			||||||
 | 
					                Expiry: new Date(darvoEnd),
 | 
				
			||||||
 | 
					                Discount: deal.Discount,
 | 
				
			||||||
 | 
					                OriginalPrice: deal.OriginalPrice,
 | 
				
			||||||
 | 
					                SalePrice: deal.SalePrice, //Math.trunc(deal.OriginalPrice * (1 - discount))
 | 
				
			||||||
 | 
					                AmountTotal: deal.AmountTotal,
 | 
				
			||||||
 | 
					                AmountSold: 0
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } while (darvoEnd < Date.now() + 6 * unixTimesInMs.minute && ++darvoIndex);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const updateWorldStateCollections = async (): Promise<void> => {
 | 
				
			||||||
 | 
					    await Promise.all([updateFissures(), updateDailyDeal()]);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -40,6 +40,7 @@ export interface IInventoryDatabase
 | 
				
			|||||||
            | "InfestedFoundry"
 | 
					            | "InfestedFoundry"
 | 
				
			||||||
            | "DialogueHistory"
 | 
					            | "DialogueHistory"
 | 
				
			||||||
            | "KubrowPetEggs"
 | 
					            | "KubrowPetEggs"
 | 
				
			||||||
 | 
					            | "KubrowPetPrints"
 | 
				
			||||||
            | "PendingCoupon"
 | 
					            | "PendingCoupon"
 | 
				
			||||||
            | "Drones"
 | 
					            | "Drones"
 | 
				
			||||||
            | "RecentVendorPurchases"
 | 
					            | "RecentVendorPurchases"
 | 
				
			||||||
@ -56,6 +57,7 @@ export interface IInventoryDatabase
 | 
				
			|||||||
            | "QualifyingInvasions"
 | 
					            | "QualifyingInvasions"
 | 
				
			||||||
            | "LastInventorySync"
 | 
					            | "LastInventorySync"
 | 
				
			||||||
            | "EndlessXP"
 | 
					            | "EndlessXP"
 | 
				
			||||||
 | 
					            | "PersonalGoalProgress"
 | 
				
			||||||
            | TEquipmentKey
 | 
					            | TEquipmentKey
 | 
				
			||||||
        >,
 | 
					        >,
 | 
				
			||||||
        InventoryDatabaseEquipment {
 | 
					        InventoryDatabaseEquipment {
 | 
				
			||||||
@ -63,7 +65,7 @@ export interface IInventoryDatabase
 | 
				
			|||||||
    Created: Date;
 | 
					    Created: Date;
 | 
				
			||||||
    TrainingDate: Date;
 | 
					    TrainingDate: Date;
 | 
				
			||||||
    LoadOutPresets: Types.ObjectId; // LoadOutPresets changed from ILoadOutPresets to Types.ObjectId for population
 | 
					    LoadOutPresets: Types.ObjectId; // LoadOutPresets changed from ILoadOutPresets to Types.ObjectId for population
 | 
				
			||||||
    Mailbox?: IMailboxDatabase;
 | 
					    //Mailbox?: IMailboxDatabase;
 | 
				
			||||||
    GuildId?: Types.ObjectId;
 | 
					    GuildId?: Types.ObjectId;
 | 
				
			||||||
    PendingRecipes: IPendingRecipeDatabase[];
 | 
					    PendingRecipes: IPendingRecipeDatabase[];
 | 
				
			||||||
    QuestKeys: IQuestKeyDatabase[];
 | 
					    QuestKeys: IQuestKeyDatabase[];
 | 
				
			||||||
@ -78,7 +80,8 @@ export interface IInventoryDatabase
 | 
				
			|||||||
    KahlLoadOuts: IOperatorConfigDatabase[];
 | 
					    KahlLoadOuts: IOperatorConfigDatabase[];
 | 
				
			||||||
    InfestedFoundry?: IInfestedFoundryDatabase;
 | 
					    InfestedFoundry?: IInfestedFoundryDatabase;
 | 
				
			||||||
    DialogueHistory?: IDialogueHistoryDatabase;
 | 
					    DialogueHistory?: IDialogueHistoryDatabase;
 | 
				
			||||||
    KubrowPetEggs?: IKubrowPetEggDatabase[];
 | 
					    KubrowPetEggs: IKubrowPetEggDatabase[];
 | 
				
			||||||
 | 
					    KubrowPetPrints: IKubrowPetPrintDatabase[];
 | 
				
			||||||
    PendingCoupon?: IPendingCouponDatabase;
 | 
					    PendingCoupon?: IPendingCouponDatabase;
 | 
				
			||||||
    Drones: IDroneDatabase[];
 | 
					    Drones: IDroneDatabase[];
 | 
				
			||||||
    RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
 | 
					    RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
 | 
				
			||||||
@ -95,6 +98,7 @@ export interface IInventoryDatabase
 | 
				
			|||||||
    QualifyingInvasions: IInvasionProgressDatabase[];
 | 
					    QualifyingInvasions: IInvasionProgressDatabase[];
 | 
				
			||||||
    LastInventorySync?: Types.ObjectId;
 | 
					    LastInventorySync?: Types.ObjectId;
 | 
				
			||||||
    EndlessXP?: IEndlessXpProgressDatabase[];
 | 
					    EndlessXP?: IEndlessXpProgressDatabase[];
 | 
				
			||||||
 | 
					    PersonalGoalProgress?: IPersonalGoalProgressDatabase[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IQuestKeyDatabase {
 | 
					export interface IQuestKeyDatabase {
 | 
				
			||||||
@ -150,9 +154,9 @@ export interface IMailboxClient {
 | 
				
			|||||||
    LastInboxId: IOid;
 | 
					    LastInboxId: IOid;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IMailboxDatabase {
 | 
					/*export interface IMailboxDatabase {
 | 
				
			||||||
    LastInboxId: Types.ObjectId;
 | 
					    LastInboxId: Types.ObjectId;
 | 
				
			||||||
}
 | 
					}*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TSolarMapRegion =
 | 
					export type TSolarMapRegion =
 | 
				
			||||||
    | "Earth"
 | 
					    | "Earth"
 | 
				
			||||||
@ -285,6 +289,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    ArchwingEnabled?: boolean;
 | 
					    ArchwingEnabled?: boolean;
 | 
				
			||||||
    PendingSpectreLoadouts?: ISpectreLoadout[];
 | 
					    PendingSpectreLoadouts?: ISpectreLoadout[];
 | 
				
			||||||
    SpectreLoadouts?: ISpectreLoadout[];
 | 
					    SpectreLoadouts?: ISpectreLoadout[];
 | 
				
			||||||
 | 
					    UsedDailyDeals: string[];
 | 
				
			||||||
    EmailItems: ITypeCount[];
 | 
					    EmailItems: ITypeCount[];
 | 
				
			||||||
    CompletedSyndicates: string[];
 | 
					    CompletedSyndicates: string[];
 | 
				
			||||||
    FocusXP?: IFocusXP;
 | 
					    FocusXP?: IFocusXP;
 | 
				
			||||||
@ -293,7 +298,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    CompletedSorties: string[];
 | 
					    CompletedSorties: string[];
 | 
				
			||||||
    LastSortieReward?: ILastSortieRewardClient[];
 | 
					    LastSortieReward?: ILastSortieRewardClient[];
 | 
				
			||||||
    LastLiteSortieReward?: ILastSortieRewardClient[];
 | 
					    LastLiteSortieReward?: ILastSortieRewardClient[];
 | 
				
			||||||
    SortieRewardAttenuation?: ISortieRewardAttenuation[];
 | 
					    SortieRewardAttenuation?: IRewardAttenuation[];
 | 
				
			||||||
    Drones: IDroneClient[];
 | 
					    Drones: IDroneClient[];
 | 
				
			||||||
    StepSequencers: IStepSequencer[];
 | 
					    StepSequencers: IStepSequencer[];
 | 
				
			||||||
    ActiveAvatarImageType?: string;
 | 
					    ActiveAvatarImageType?: string;
 | 
				
			||||||
@ -304,9 +309,9 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    FocusUpgrades: IFocusUpgrade[];
 | 
					    FocusUpgrades: IFocusUpgrade[];
 | 
				
			||||||
    HasContributedToDojo?: boolean;
 | 
					    HasContributedToDojo?: boolean;
 | 
				
			||||||
    HWIDProtectEnabled?: boolean;
 | 
					    HWIDProtectEnabled?: boolean;
 | 
				
			||||||
    //KubrowPetPrints: IKubrowPetPrint[];
 | 
					    KubrowPetPrints: IKubrowPetPrintClient[];
 | 
				
			||||||
    AlignmentReplay?: IAlignment;
 | 
					    AlignmentReplay?: IAlignment;
 | 
				
			||||||
    //PersonalGoalProgress: IPersonalGoalProgress[];
 | 
					    PersonalGoalProgress?: IPersonalGoalProgressClient[];
 | 
				
			||||||
    ThemeStyle: string;
 | 
					    ThemeStyle: string;
 | 
				
			||||||
    ThemeBackground: string;
 | 
					    ThemeBackground: string;
 | 
				
			||||||
    ThemeSounds: string;
 | 
					    ThemeSounds: string;
 | 
				
			||||||
@ -349,7 +354,6 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    //LeagueTickets: any[];
 | 
					    //LeagueTickets: any[];
 | 
				
			||||||
    //Quests: any[];
 | 
					    //Quests: any[];
 | 
				
			||||||
    //Robotics: any[];
 | 
					    //Robotics: any[];
 | 
				
			||||||
    //UsedDailyDeals: any[];
 | 
					 | 
				
			||||||
    LibraryPersonalTarget?: string;
 | 
					    LibraryPersonalTarget?: string;
 | 
				
			||||||
    LibraryPersonalProgress: ILibraryPersonalProgress[];
 | 
					    LibraryPersonalProgress: ILibraryPersonalProgress[];
 | 
				
			||||||
    CollectibleSeries?: ICollectibleEntry[];
 | 
					    CollectibleSeries?: ICollectibleEntry[];
 | 
				
			||||||
@ -378,6 +382,8 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
 | 
				
			|||||||
    LockedWeaponGroup?: ILockedWeaponGroupClient;
 | 
					    LockedWeaponGroup?: ILockedWeaponGroupClient;
 | 
				
			||||||
    HubNpcCustomizations?: IHubNpcCustomization[];
 | 
					    HubNpcCustomizations?: IHubNpcCustomization[];
 | 
				
			||||||
    Ship?: IOrbiter; // U22 and below, response only
 | 
					    Ship?: IOrbiter; // U22 and below, response only
 | 
				
			||||||
 | 
					    ClaimedJunctionChallengeRewards?: string[]; // U39
 | 
				
			||||||
 | 
					    SpecialItemRewardAttenuation?: IRewardAttenuation[]; // Baro's Void Surplus
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IAffiliation {
 | 
					export interface IAffiliation {
 | 
				
			||||||
@ -446,8 +452,9 @@ export interface IVendorPurchaseHistoryEntryDatabase {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export interface IChallengeProgress {
 | 
					export interface IChallengeProgress {
 | 
				
			||||||
    Progress: number;
 | 
					    Progress: number;
 | 
				
			||||||
    Name: string;
 | 
					 | 
				
			||||||
    Completed?: string[];
 | 
					    Completed?: string[];
 | 
				
			||||||
 | 
					    ReceivedJunctionReward?: boolean; // U39
 | 
				
			||||||
 | 
					    Name: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ICollectibleEntry {
 | 
					export interface ICollectibleEntry {
 | 
				
			||||||
@ -718,8 +725,8 @@ export interface IKubrowPetEggDatabase {
 | 
				
			|||||||
    _id: Types.ObjectId;
 | 
					    _id: Types.ObjectId;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IKubrowPetPrint {
 | 
					export interface IKubrowPetPrintClient {
 | 
				
			||||||
    ItemType: KubrowPetPrintItemType;
 | 
					    ItemType: "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint";
 | 
				
			||||||
    Name: string;
 | 
					    Name: string;
 | 
				
			||||||
    IsMale: boolean;
 | 
					    IsMale: boolean;
 | 
				
			||||||
    Size: number; // seems to be 0.7 to 1.0
 | 
					    Size: number; // seems to be 0.7 to 1.0
 | 
				
			||||||
@ -729,6 +736,10 @@ export interface IKubrowPetPrint {
 | 
				
			|||||||
    InheritedModularParts?: any[];
 | 
					    InheritedModularParts?: any[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IKubrowPetPrintDatabase extends Omit<IKubrowPetPrintClient, "ItemId" | "InheritedModularParts"> {
 | 
				
			||||||
 | 
					    _id: Types.ObjectId;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ITraits {
 | 
					export interface ITraits {
 | 
				
			||||||
    BaseColor: string;
 | 
					    BaseColor: string;
 | 
				
			||||||
    SecondaryColor: string;
 | 
					    SecondaryColor: string;
 | 
				
			||||||
@ -742,15 +753,11 @@ export interface ITraits {
 | 
				
			|||||||
    Tail?: string;
 | 
					    Tail?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum KubrowPetPrintItemType {
 | 
					 | 
				
			||||||
    LotusTypesGameKubrowPetImprintedTraitPrint = "/Lotus/Types/Game/KubrowPet/ImprintedTraitPrint"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export interface IKubrowPetDetailsDatabase {
 | 
					export interface IKubrowPetDetailsDatabase {
 | 
				
			||||||
    Name?: string;
 | 
					    Name?: string;
 | 
				
			||||||
    IsPuppy?: boolean;
 | 
					    IsPuppy?: boolean;
 | 
				
			||||||
    HasCollar: boolean;
 | 
					    HasCollar: boolean;
 | 
				
			||||||
    PrintsRemaining?: number;
 | 
					    PrintsRemaining: number;
 | 
				
			||||||
    Status: Status;
 | 
					    Status: Status;
 | 
				
			||||||
    HatchDate?: Date;
 | 
					    HatchDate?: Date;
 | 
				
			||||||
    DominantTraits: ITraits;
 | 
					    DominantTraits: ITraits;
 | 
				
			||||||
@ -779,7 +786,7 @@ export interface ILastSortieRewardDatabase extends Omit<ILastSortieRewardClient,
 | 
				
			|||||||
    SortieId: Types.ObjectId;
 | 
					    SortieId: Types.ObjectId;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ISortieRewardAttenuation {
 | 
					export interface IRewardAttenuation {
 | 
				
			||||||
    Tag: string;
 | 
					    Tag: string;
 | 
				
			||||||
    Atten: number;
 | 
					    Atten: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1015,13 +1022,17 @@ export interface IPeriodicMissionCompletionResponse extends Omit<IPeriodicMissio
 | 
				
			|||||||
    date: IMongoDate;
 | 
					    date: IMongoDate;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IPersonalGoalProgress {
 | 
					export interface IPersonalGoalProgressClient {
 | 
				
			||||||
 | 
					    Best: number;
 | 
				
			||||||
    Count: number;
 | 
					    Count: number;
 | 
				
			||||||
    Tag: string;
 | 
					    Tag: string;
 | 
				
			||||||
    Best?: number;
 | 
					 | 
				
			||||||
    _id: IOid;
 | 
					    _id: IOid;
 | 
				
			||||||
    ReceivedClanReward0?: boolean;
 | 
					    //ReceivedClanReward0?: boolean;
 | 
				
			||||||
    ReceivedClanReward1?: boolean;
 | 
					    //ReceivedClanReward1?: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IPersonalGoalProgressDatabase extends Omit<IPersonalGoalProgressClient, "_id"> {
 | 
				
			||||||
 | 
					    goalId: Types.ObjectId;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IPersonalTechProjectDatabase {
 | 
					export interface IPersonalTechProjectDatabase {
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ export interface IDatabaseAccount extends IDatabaseAccountRequiredFields {
 | 
				
			|||||||
    LatestEventMessageDate: Date;
 | 
					    LatestEventMessageDate: Date;
 | 
				
			||||||
    LastLoginRewardDate: number;
 | 
					    LastLoginRewardDate: number;
 | 
				
			||||||
    LoginDays: number;
 | 
					    LoginDays: number;
 | 
				
			||||||
 | 
					    DailyFirstWinDate: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Includes virtual ID
 | 
					// Includes virtual ID
 | 
				
			||||||
 | 
				
			|||||||
@ -17,9 +17,9 @@ export interface IMissionReward {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IMissionCredits {
 | 
					export interface IMissionCredits {
 | 
				
			||||||
    MissionCredits: number[];
 | 
					    MissionCredits: [number, number];
 | 
				
			||||||
    CreditBonus: number[];
 | 
					    CreditsBonus: [number, number]; // "Credit Reward"; `CreditsBonus[1]` is `CreditsBonus[0] * 2` if DailyMissionBonus
 | 
				
			||||||
    TotalCredits: number[];
 | 
					    TotalCredits: [number, number];
 | 
				
			||||||
    DailyMissionBonus?: boolean;
 | 
					    DailyMissionBonus?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,8 @@ import {
 | 
				
			|||||||
    ITypeCount,
 | 
					    ITypeCount,
 | 
				
			||||||
    IRecentVendorPurchaseClient,
 | 
					    IRecentVendorPurchaseClient,
 | 
				
			||||||
    TEquipmentKey,
 | 
					    TEquipmentKey,
 | 
				
			||||||
    ICrewMemberClient
 | 
					    ICrewMemberClient,
 | 
				
			||||||
 | 
					    IKubrowPetPrintClient
 | 
				
			||||||
} from "./inventoryTypes/inventoryTypes";
 | 
					} from "./inventoryTypes/inventoryTypes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export enum PurchaseSource {
 | 
					export enum PurchaseSource {
 | 
				
			||||||
@ -78,6 +79,7 @@ export type IInventoryChanges = {
 | 
				
			|||||||
    NewVendorPurchase?: IRecentVendorPurchaseClient; // >= 38.5.0
 | 
					    NewVendorPurchase?: IRecentVendorPurchaseClient; // >= 38.5.0
 | 
				
			||||||
    RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
 | 
					    RecentVendorPurchases?: IRecentVendorPurchaseClient; // < 38.5.0
 | 
				
			||||||
    CrewMembers?: ICrewMemberClient[];
 | 
					    CrewMembers?: ICrewMemberClient[];
 | 
				
			||||||
 | 
					    KubrowPetPrints?: IKubrowPetPrintClient[];
 | 
				
			||||||
} & Record<
 | 
					} & Record<
 | 
				
			||||||
        Exclude<
 | 
					        Exclude<
 | 
				
			||||||
            string,
 | 
					            string,
 | 
				
			||||||
@ -105,6 +107,7 @@ export interface IPurchaseResponse {
 | 
				
			|||||||
    Standing?: IAffiliationMods[];
 | 
					    Standing?: IAffiliationMods[];
 | 
				
			||||||
    FreeFavorsUsed?: IAffiliationMods[];
 | 
					    FreeFavorsUsed?: IAffiliationMods[];
 | 
				
			||||||
    BoosterPackItems?: string;
 | 
					    BoosterPackItems?: string;
 | 
				
			||||||
 | 
					    DailyDealUsed?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type IBinChanges = {
 | 
					export type IBinChanges = {
 | 
				
			||||||
 | 
				
			|||||||
@ -139,6 +139,14 @@ export type IMissionInventoryUpdateRequest = {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
    wagerTier?: number; // the index
 | 
					    wagerTier?: number; // the index
 | 
				
			||||||
    creditsFee?: number; // the index
 | 
					    creditsFee?: number; // the index
 | 
				
			||||||
 | 
					    GoalProgress?: {
 | 
				
			||||||
 | 
					        _id: IOid;
 | 
				
			||||||
 | 
					        Count: number;
 | 
				
			||||||
 | 
					        Best: number;
 | 
				
			||||||
 | 
					        Tag: string;
 | 
				
			||||||
 | 
					        IsMultiProgress: boolean;
 | 
				
			||||||
 | 
					        MultiProgress: unknown[];
 | 
				
			||||||
 | 
					    }[];
 | 
				
			||||||
    InvasionProgress?: IInvasionProgressClient[];
 | 
					    InvasionProgress?: IInvasionProgressClient[];
 | 
				
			||||||
    ConquestMissionsCompleted?: number;
 | 
					    ConquestMissionsCompleted?: number;
 | 
				
			||||||
    duviriSuitSelection?: string;
 | 
					    duviriSuitSelection?: string;
 | 
				
			||||||
@ -156,6 +164,8 @@ export type IMissionInventoryUpdateRequest = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export interface IRewardInfo {
 | 
					export interface IRewardInfo {
 | 
				
			||||||
    node: string;
 | 
					    node: string;
 | 
				
			||||||
 | 
					    goalId?: string;
 | 
				
			||||||
 | 
					    goalManifest?: string;
 | 
				
			||||||
    invasionId?: string;
 | 
					    invasionId?: string;
 | 
				
			||||||
    invasionAllyFaction?: "FC_GRINEER" | "FC_CORPUS";
 | 
					    invasionAllyFaction?: "FC_GRINEER" | "FC_CORPUS";
 | 
				
			||||||
    sortieId?: string;
 | 
					    sortieId?: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -96,6 +96,7 @@ export interface IPlacedDecosDatabase {
 | 
				
			|||||||
    Pos: [number, number, number];
 | 
					    Pos: [number, number, number];
 | 
				
			||||||
    Rot: [number, number, number];
 | 
					    Rot: [number, number, number];
 | 
				
			||||||
    Scale?: number;
 | 
					    Scale?: number;
 | 
				
			||||||
 | 
					    Sockets?: number;
 | 
				
			||||||
    PictureFrameInfo?: IPictureFrameInfo;
 | 
					    PictureFrameInfo?: IPictureFrameInfo;
 | 
				
			||||||
    _id: Types.ObjectId;
 | 
					    _id: Types.ObjectId;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -136,6 +137,7 @@ export interface IShipDecorationsRequest {
 | 
				
			|||||||
    MoveId?: string;
 | 
					    MoveId?: string;
 | 
				
			||||||
    OldRoom?: string;
 | 
					    OldRoom?: string;
 | 
				
			||||||
    Scale?: number;
 | 
					    Scale?: number;
 | 
				
			||||||
 | 
					    Sockets?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IShipDecorationsResponse {
 | 
					export interface IShipDecorationsResponse {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					import { IMissionReward } from "warframe-public-export-plus";
 | 
				
			||||||
import { IMongoDate, IOid } from "./commonTypes";
 | 
					import { IMongoDate, IOid } from "./commonTypes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IWorldState {
 | 
					export interface IWorldState {
 | 
				
			||||||
@ -12,7 +13,9 @@ export interface IWorldState {
 | 
				
			|||||||
    ActiveMissions: IFissure[];
 | 
					    ActiveMissions: IFissure[];
 | 
				
			||||||
    GlobalUpgrades: IGlobalUpgrade[];
 | 
					    GlobalUpgrades: IGlobalUpgrade[];
 | 
				
			||||||
    NodeOverrides: INodeOverride[];
 | 
					    NodeOverrides: INodeOverride[];
 | 
				
			||||||
 | 
					    VoidTraders: IVoidTrader[];
 | 
				
			||||||
    VoidStorms: IVoidStorm[];
 | 
					    VoidStorms: IVoidStorm[];
 | 
				
			||||||
 | 
					    DailyDeals: IDailyDeal[];
 | 
				
			||||||
    PVPChallengeInstances: IPVPChallengeInstance[];
 | 
					    PVPChallengeInstances: IPVPChallengeInstance[];
 | 
				
			||||||
    EndlessXpChoices: IEndlessXpChoice[];
 | 
					    EndlessXpChoices: IEndlessXpChoice[];
 | 
				
			||||||
    SeasonInfo?: {
 | 
					    SeasonInfo?: {
 | 
				
			||||||
@ -36,11 +39,15 @@ export interface IGoal {
 | 
				
			|||||||
    Goal: number;
 | 
					    Goal: number;
 | 
				
			||||||
    Success: number;
 | 
					    Success: number;
 | 
				
			||||||
    Personal: boolean;
 | 
					    Personal: boolean;
 | 
				
			||||||
 | 
					    Bounty?: boolean;
 | 
				
			||||||
 | 
					    ClampNodeScores?: boolean;
 | 
				
			||||||
    Desc: string;
 | 
					    Desc: string;
 | 
				
			||||||
    ToolTip: string;
 | 
					    ToolTip?: string;
 | 
				
			||||||
    Icon: string;
 | 
					    Icon: string;
 | 
				
			||||||
    Tag: string;
 | 
					    Tag: string;
 | 
				
			||||||
    Node: string;
 | 
					    Node: string;
 | 
				
			||||||
 | 
					    MissionKeyName?: string;
 | 
				
			||||||
 | 
					    Reward?: IMissionReward;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ISyndicateMissionInfo {
 | 
					export interface ISyndicateMissionInfo {
 | 
				
			||||||
@ -140,6 +147,22 @@ export interface ILiteSortie {
 | 
				
			|||||||
    }[];
 | 
					    }[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IVoidTrader {
 | 
				
			||||||
 | 
					    _id: IOid;
 | 
				
			||||||
 | 
					    Activation: IMongoDate;
 | 
				
			||||||
 | 
					    Expiry: IMongoDate;
 | 
				
			||||||
 | 
					    Character: string;
 | 
				
			||||||
 | 
					    Node: string;
 | 
				
			||||||
 | 
					    Manifest: IVoidTraderOffer[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IVoidTraderOffer {
 | 
				
			||||||
 | 
					    ItemType: string;
 | 
				
			||||||
 | 
					    PrimePrice: number;
 | 
				
			||||||
 | 
					    RegularPrice: number;
 | 
				
			||||||
 | 
					    Limit?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IVoidStorm {
 | 
					export interface IVoidStorm {
 | 
				
			||||||
    _id: IOid;
 | 
					    _id: IOid;
 | 
				
			||||||
    Node: string;
 | 
					    Node: string;
 | 
				
			||||||
@ -148,6 +171,28 @@ export interface IVoidStorm {
 | 
				
			|||||||
    ActiveMissionTier: string;
 | 
					    ActiveMissionTier: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IDailyDeal {
 | 
				
			||||||
 | 
					    StoreItem: string;
 | 
				
			||||||
 | 
					    Activation: IMongoDate;
 | 
				
			||||||
 | 
					    Expiry: IMongoDate;
 | 
				
			||||||
 | 
					    Discount: number;
 | 
				
			||||||
 | 
					    OriginalPrice: number;
 | 
				
			||||||
 | 
					    SalePrice: number;
 | 
				
			||||||
 | 
					    AmountTotal: number;
 | 
				
			||||||
 | 
					    AmountSold: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IDailyDealDatabase {
 | 
				
			||||||
 | 
					    StoreItem: string;
 | 
				
			||||||
 | 
					    Activation: Date;
 | 
				
			||||||
 | 
					    Expiry: Date;
 | 
				
			||||||
 | 
					    Discount: number;
 | 
				
			||||||
 | 
					    OriginalPrice: number;
 | 
				
			||||||
 | 
					    SalePrice: number;
 | 
				
			||||||
 | 
					    AmountTotal: number;
 | 
				
			||||||
 | 
					    AmountSold: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IPVPChallengeInstance {
 | 
					export interface IPVPChallengeInstance {
 | 
				
			||||||
    _id: IOid;
 | 
					    _id: IOid;
 | 
				
			||||||
    challengeTypeRefID: string;
 | 
					    challengeTypeRefID: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,10 @@
 | 
				
			|||||||
  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophyGoldARecipe",
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophyGoldARecipe",
 | 
				
			||||||
  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophyPlatinumARecipe",
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophyPlatinumARecipe",
 | 
				
			||||||
  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophySilverARecipe",
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DojoRemasterTrophySilverARecipe",
 | 
				
			||||||
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventBronzeTrophyRecipe",
 | 
				
			||||||
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventClayTrophyRecipe",
 | 
				
			||||||
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventGoldTrophyRecipe",
 | 
				
			||||||
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/DuviriMurmurEventSilverTrophyRecipe",
 | 
				
			||||||
  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventBaseTrophyRecipe",
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventBaseTrophyRecipe",
 | 
				
			||||||
  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventBronzeTrophyRecipe",
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventBronzeTrophyRecipe",
 | 
				
			||||||
  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventGoldTrophyRecipe",
 | 
					  "/Lotus/Levels/ClanDojo/ComponentPropRecipes/EntratiEventGoldTrophyRecipe",
 | 
				
			||||||
 | 
				
			|||||||
@ -45,5 +45,6 @@
 | 
				
			|||||||
  "/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon",
 | 
					  "/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon",
 | 
				
			||||||
  "/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol",
 | 
					  "/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol",
 | 
				
			||||||
  "/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon",
 | 
					  "/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon",
 | 
				
			||||||
  "/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon"
 | 
					  "/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon",
 | 
				
			||||||
 | 
					  "/Lotus/Weapons/Tenno/Zariman/Melee/HeavyScythe/ZarimanHeavyScythe/ZarimanHeavyScytheWeapon"
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "Messages": [
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      "sub": "Welcome to Space Ninja Server",
 | 
					 | 
				
			||||||
      "sndr": "/Lotus/Language/Bosses/Ordis",
 | 
					 | 
				
			||||||
      "msg": "Enjoy your Space Ninja Experience",
 | 
					 | 
				
			||||||
      "icon": "/Lotus/Interface/Icons/Npcs/Ordis.png",
 | 
					 | 
				
			||||||
      "eventMessageDate": "2025-01-30T13:00:00.000Z",
 | 
					 | 
				
			||||||
      "r": false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										414
									
								
								static/fixed_responses/worldState/baro.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								static/fixed_responses/worldState/baro.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,414 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "evergreen": [
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Keys/MummyQuestKeyBlueprint", "PrimePrice": 100, "RegularPrice": 25000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/FootstepsMaple", "PrimePrice": 15, "RegularPrice": 1000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/BoosterPacks/BaroTreasureBox", "PrimePrice": 0, "RegularPrice": 50000, "Limit": 1 }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "armorSets": [
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmour/BaroArmourA", "PrimePrice": 350, "RegularPrice": 110000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmour/BaroArmourC", "PrimePrice": 150, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmour/BaroArmourL", "PrimePrice": 300, "RegularPrice": 150000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmourTwo/BaroArmourTwoA", "PrimePrice": 310, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmourTwo/BaroArmourTwoC", "PrimePrice": 175, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmourTwo/BaroArmourTwoL", "PrimePrice": 225, "RegularPrice": 150000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmourThree/BaroArmourThreeA", "PrimePrice": 400, "RegularPrice": 350000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmourThree/BaroArmourThreeC", "PrimePrice": 350, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/BaroArmourThree/BaroArmourThreeL", "PrimePrice": 400, "RegularPrice": 350000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/FurisArmor/PrismaFurisAArmor", "PrimePrice": 300, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/FurisArmor/PrismaFurisCArmor", "PrimePrice": 250, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/FurisArmor/PrismaFurisLArmor", "PrimePrice": 225, "RegularPrice": 175000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/GrineerTurbines/WraithTurbinesArmArmor", "PrimePrice": 350, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/GrineerTurbines/WraithTurbinesChestArmor", "PrimePrice": 300, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/GrineerTurbines/WraithTurbinesLegArmor", "PrimePrice": 350, "RegularPrice": 150000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/NecraArmor/NecraArmorA", "PrimePrice": 315, "RegularPrice": 215000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/NecraArmor/NecraArmorC", "PrimePrice": 325, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/NecraArmor/NecraArmorL", "PrimePrice": 300, "RegularPrice": 200000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetThreeWinged/VTSetThreeArmLeftArmor", "PrimePrice": 65, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetThreeWinged/VTSetThreeArmRightArmor", "PrimePrice": 65, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetThreeWinged/VTSetThreeChestArmor", "PrimePrice": 150, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetThreeWinged/VTSetThreeLegLeftArmor", "PrimePrice": 65, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetThreeWinged/VTSetThreeLegRightArmor", "PrimePrice": 65, "RegularPrice": 75000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetTwoSamurai/VTSetTwoArmLeftArmor", "PrimePrice": 100, "RegularPrice": 55000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetTwoSamurai/VTSetTwoArmRightArmor", "PrimePrice": 100, "RegularPrice": 55000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetTwoSamurai/VTSetTwoChestArmor", "PrimePrice": 225, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetTwoSamurai/VTSetTwoLegLeftArmor", "PrimePrice": 100, "RegularPrice": 55000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/SetTwoSamurai/VTSetTwoLegRightArmor", "PrimePrice": 100, "RegularPrice": 55000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnLatronArmor/TnLatronArmArmorElixis", "PrimePrice": 325, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnLatronArmor/TnLatronArmArmorPrisma", "PrimePrice": 325, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnLatronArmor/TnLatronChestArmorElixis", "PrimePrice": 275, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnLatronArmor/TnLatronChestArmorPrisma", "PrimePrice": 275, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnLatronArmor/TnLatronLegArmorElixis", "PrimePrice": 300, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnLatronArmor/TnLatronLegArmorPrisma", "PrimePrice": 300, "RegularPrice": 175000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnShinaiArmor/TnShinaiArmorA", "PrimePrice": 315, "RegularPrice": 125000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnShinaiArmor/TnShinaiArmorC", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					      { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/TnShinaiArmor/TnShinaiArmorL", "PrimePrice": 275, "RegularPrice": 115000 }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      [{ "ItemType": "/Lotus/Types/StoreItems/Packages/VTEosArmourBundle", "PrimePrice": 285, "RegularPrice": 260000 }],
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/VTEos/VTEosALArmor", "PrimePrice": 50, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					        { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/VTEos/VTEosARArmor", "PrimePrice": 50, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					        { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/VTEos/VTEosChestArmor", "PrimePrice": 125, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					        { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/VTEos/VTEosLLArmor", "PrimePrice": 65, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					        { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/VTEos/VTEosLRArmor", "PrimePrice": 65, "RegularPrice": 50000 }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "rest": [
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Armor/Halloween2014Wings/PrismaNaberusArmArmor", "PrimePrice": 220, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/TennoCon2024GlyphAlt", "PrimePrice": 15, "RegularPrice": 1000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/Emotes/Tennocon2024EmoteAlt", "PrimePrice": 15, "RegularPrice": 1000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/HeartOfDeimosAlbumCoverPoster", "PrimePrice": 80, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConC", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConJ", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConH", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Game/Projections/T3VoidProjectionVoltOdonataPrimeBronze", "PrimePrice": 125, "RegularPrice": 55000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Game/Projections/T4VoidProjectionVoltOdonataPrimeBronze", "PrimePrice": 125, "RegularPrice": 55000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Game/Projections/T4VoidProjectionMagNovaVaultBBronze", "PrimePrice": 125, "RegularPrice": 55000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/SolsticeNelumboCape", "PrimePrice": 325, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/SummerSolstice/SummerSolsticeTwinGrakatas", "PrimePrice": 300, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Weapons/Staff/TnRibbonStaffSkin", "PrimePrice": 350, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/Melee/GunBlade/GrnGunBlade/GrnGunblade", "PrimePrice": 550, "RegularPrice": 325000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/LongGuns/CrpBFG/Vandal/VandalCrpBFG", "PrimePrice": 650, "RegularPrice": 550000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Event/AmbulasEvent/Expert/SecondaryExplosionRadiusModExpert", "PrimePrice": 350, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/Dragon2024BadgeItem", "PrimePrice": 55, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Archwing/Rifle/PrimedArchwingDamageOnReloadMod", "PrimePrice": 375, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Archwing/Rifle/PrimedArchwingRifleFireIterationsMod", "PrimePrice": 400, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageBaruukDoanStyle", "PrimePrice": 75, "RegularPrice": 60000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/OctaviaBobbleHead", "PrimePrice": 50, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Skins/GaussSentinelSkin", "PrimePrice": 500, "RegularPrice": 425000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/PrismaLotusVinesSigil", "PrimePrice": 55, "RegularPrice": 60000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageExcaliburActionProto", "PrimePrice": 75, "RegularPrice": 60000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageIvaraAction", "PrimePrice": 75, "RegularPrice": 60000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/HornSkullScarf", "PrimePrice": 325, "RegularPrice": 350000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/RhinoDeluxeSigil", "PrimePrice": 45, "RegularPrice": 55000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Events/InfQuantaInfestedAladV", "PrimePrice": 325, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/JavisExperimentsPosterD", "PrimePrice": 90, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/JavisExperimentsPosterB", "PrimePrice": 90, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/JavisExperimentsPosterC", "PrimePrice": 90, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/PrimedWeaponElectricityDamageMod", "PrimePrice": 350, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/Expert/AvatarShieldMaxModExpert", "PrimePrice": 350, "RegularPrice": 225000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/JavisExperimentsPosterA", "PrimePrice": 90, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/EventSigilScarletSpear", "PrimePrice": 45, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrnOrokinRifle/GrnOrokinRifleWeapon", "PrimePrice": 675, "RegularPrice": 625000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/ElixisNikana", "PrimePrice": 375, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Wings/GaussSentinelWings", "PrimePrice": 400, "RegularPrice": 500000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Tails/GaussSentinelTail", "PrimePrice": 400, "RegularPrice": 500000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Masks/GaussSentinelMask", "PrimePrice": 450, "RegularPrice": 400000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropGrineerCutter", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/CNY2023EmblemItem", "PrimePrice": 55, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/WeGameNewYearFreeTigerSigil", "PrimePrice": 55, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/CNY2022EmblemItem", "PrimePrice": 55, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Leverian/IvaraLeverianPovisRecordsDecoration", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Hoods/HoodDuviriOperator", "PrimePrice": 550, "RegularPrice": 500000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/Melee/CrpTonfa/CrpPrismaTonfa", "PrimePrice": 450, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropCleaningDroneDuviri", "PrimePrice": 800, "RegularPrice": 650000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Accessories/AshLevarianTiara", "PrimePrice": 550, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/BaroEphemeraB", "PrimePrice": 250, "RegularPrice": 350000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Promo/Warframe/PromoParis", "PrimePrice": 315, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/ThraxSigil", "PrimePrice": 50, "RegularPrice": 55000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/PrismaLenz/PrismaLenzWeapon", "PrimePrice": 575, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Vignettes/Warframes/ArchwingAFItem", "PrimePrice": 100, "RegularPrice": 330000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Game/QuartersWallpapers/LavosAlchemistWallpaper", "PrimePrice": 275, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/GrendelOrokinDishSet", "PrimePrice": 110, "RegularPrice": 130000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/SuitCustomizations/ColourPickerKiteerItemB", "PrimePrice": 200, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/NezhaEtchingsTablets", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/GaussTowerOfAltraDeco", "PrimePrice": 110, "RegularPrice": 125000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroPlanter", "PrimePrice": 125, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroPedestal", "PrimePrice": 150, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Leggings/LeggingsNovaEngineer", "PrimePrice": 300, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/BodySuits/BodySuitNovaEngineer", "PrimePrice": 300, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Sleeves/SleevesNovaEngineer", "PrimePrice": 300, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Hoods/HoodNovaEngineer", "PrimePrice": 350, "RegularPrice": 375000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BekranZaftBucketBroom", "PrimePrice": 100, "RegularPrice": 125000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Tenno/Melee/Warfan/TnMoonWarfan/MoonWarfanWeapon", "PrimePrice": 410, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/MeleeDangles/MoonWarfanSugatraMeleeDangle", "PrimePrice": 250, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/OstronHeadStatue", "PrimePrice": 125, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/DomsFinalDrink", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Wisp/WispAlternateSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Pacifist/BaruukImmortalSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/ErraBobbleHead", "PrimePrice": 75, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/OwlOrdisStatue", "PrimePrice": 350, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TNWVesoBobbleHead", "PrimePrice": 75, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TNWTeshinBobbleHead", "PrimePrice": 75, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/CosmeticEnhancers/Peculiars/EvilSpiritMod", "PrimePrice": 250, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/BaroCape3Scarf", "PrimePrice": 500, "RegularPrice": 500000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/ElixisTiberon", "PrimePrice": 315, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/LotusFlowers", "PrimePrice": 250, "RegularPrice": 450000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/UmbraPedestal", "PrimePrice": 0, "RegularPrice": 1000000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Dragon/ChromaAlternateSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Hoverboard/HoverboardStickerBaroB", "PrimePrice": 75, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/ElixisLatronPistol", "PrimePrice": 400, "RegularPrice": 215000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/WeaponGlaiveOnKillBuffSecondary", "PrimePrice": 300, "RegularPrice": 115000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConA", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/WeaponGlaiveSecondaryHeadshotKillMod", "PrimePrice": 300, "RegularPrice": 115000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConD", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConB", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponIncreaseRadialExplosionModExpert", "PrimePrice": 350, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Tenno/Archwing/Primary/ArchwingHeavyPistols/Prisma/PrismaArchHeavyPistols", "PrimePrice": 525, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/TwinSnakesGlyph", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConF", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConE", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/WeaponGlaiveOnSixKillsBuffSecondary", "PrimePrice": 300, "RegularPrice": 115000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/WeGameNewYearOxSigil", "PrimePrice": 55, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConG", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponFreezeDamageModExpert", "PrimePrice": 350, "RegularPrice": 125000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/GarvLatroxPoster", "PrimePrice": 80, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrnBoomerang/HalikarWraithWeapon", "PrimePrice": 450, "RegularPrice": 350000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/TarotCardTennoConI", "PrimePrice": 75, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponReloadSpeedModExpert", "PrimePrice": 300, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/PrismaMachete", "PrimePrice": 400, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/MoaPet/BaroMoaPetSkin", "PrimePrice": 500, "RegularPrice": 325000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/Deimos/PlushySunMonsterCommon", "PrimePrice": 150, "RegularPrice": 125000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/Deimos/PlushyMoonMonsterCommon", "PrimePrice": 150, "RegularPrice": 125000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponClipMaxModExpert", "PrimePrice": 280, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponClipMaxModExpert", "PrimePrice": 280, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/TnShinaiSword/TnShinaiSwordSkin", "PrimePrice": 375, "RegularPrice": 280000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Weapons/DualSword/DualRibbonKamasSkin", "PrimePrice": 350, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Infestation/NidusAlternateSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Game/ActionFigureDioramas/EmpyreanRegionADiorama", "PrimePrice": 155, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropGrineerFlak", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropGrineerTaktis", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/AshLeverianLiosPistol", "PrimePrice": 400, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Glass/GaraAlternateSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponSnipersConvertAmmoModExpert", "PrimePrice": 400, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/EraHypnosisPoster", "PrimePrice": 100, "RegularPrice": 110000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/NezhaLeverianCape", "PrimePrice": 400, "RegularPrice": 350000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Leverian/NezhaLeverian/NezhaLeverianPolearm", "PrimePrice": 350, "RegularPrice": 325000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BoredTennoPoster", "PrimePrice": 90, "RegularPrice": 120000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropCorpusBasilisk", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropCorpusWeaver", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropCorpusHarpi", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Archwing/GrendelArchwingSkin", "PrimePrice": 400, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Hoods/JaviExecutionHood", "PrimePrice": 450, "RegularPrice": 450000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/DualStat/ElectEventMeleeMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/DualStat/FireEventMeleeMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Tenno/Melee/MeleeTrees/ClawCmbTwoMeleeTree", "PrimePrice": 385, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/DualStat/FireEventRifleMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Tenno/Melee/MeleeTrees/AxeCmbThreeMeleeTree", "PrimePrice": 385, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/WeaponEventSlashDamageMod", "PrimePrice": 375, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/BowMultiShotOnHitMod", "PrimePrice": 300, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/DualStat/ElectEventShotgunMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/DualStat/FireEventPistolMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/DualStat/FireEventShotgunMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/WeaponEventPistolImpactDamageMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/PrimedWeaponCritDamageMod", "PrimePrice": 400, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeFactionDamageInfestedExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeFactionDamageGrineerExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeFactionDamageCorruptedExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeFactionDamageCorpusExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponClipMaxModExpert", "PrimePrice": 280, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponShotgunConvertAmmoModExpert", "PrimePrice": 400, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Archwing/Rifle/Expert/ArchwingRifleDamageAmountModExpert", "PrimePrice": 350, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponRifleConvertAmmoModExpert", "PrimePrice": 400, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Sentinels/SentinelPrecepts/PrimedRegen", "PrimePrice": 300, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeRangeIncModExpert", "PrimePrice": 300, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponCritDamageModExpert", "PrimePrice": 280, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponReloadSpeedModExpert", "PrimePrice": 375, "RegularPrice": 120000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponMeleeDamageModExpert", "PrimePrice": 385, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponDamageAmountModExpert", "PrimePrice": 300, "RegularPrice": 110000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponCritChanceModBeginnerExpert", "PrimePrice": 400, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponPistolConvertAmmoModExpert", "PrimePrice": 400, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Sentinel/Kubrow/Expert/KubrowPackLeaderExpertMod", "PrimePrice": 300, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Archwing/Expert/ArchwingSuitAbilityStrengthModExpert", "PrimePrice": 350, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponImpactDamageModExpert", "PrimePrice": 350, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponFireDamageModExpert", "PrimePrice": 350, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/Expert/AvatarPowerMaxModExpert", "PrimePrice": 350, "RegularPrice": 110000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/Expert/WeaponToxinDamageModExpert", "PrimePrice": 350, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponReloadSpeedModExpert", "PrimePrice": 375, "RegularPrice": 120000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponPistolFactionDamageInfestedExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponPistolFactionDamageGrineerExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponPistolFactionDamageCorruptedExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/Expert/WeaponPistolFactionDamageCorpusExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/WeaponFreezeDamageModExpert", "PrimePrice": 350, "RegularPrice": 110000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Warframe/Expert/AvatarAbilityDurationModExpert", "PrimePrice": 350, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponShotgunFactionDamageInfestedExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponShotgunFactionDamageGrineerExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponShotgunFactionDamageCorruptedExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponShotgunFactionDamageCorpusExpert", "PrimePrice": 350, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/Expert/WeaponElectricityDamageModExpert", "PrimePrice": 350, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/PrimedWeaponFactionDamageInfested", "PrimePrice": 400, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/PrimedWeaponFactionDamageGrineer", "PrimePrice": 400, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/PrimedWeaponFactionDamageCorrupted", "PrimePrice": 400, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/Expert/PrimedWeaponFactionDamageCorpus", "PrimePrice": 400, "RegularPrice": 140000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Sentinel/SentinelLootRadarEnemyRadarExpertMod", "PrimePrice": 300, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Tenno/Melee/MeleeTrees/GlaiveCmbTwoMeleeTree", "PrimePrice": 385, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/WeaponEventSlashDamageMod", "PrimePrice": 375, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Melee/WeaponEventMeleeImpactDamageMod", "PrimePrice": 400, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/WeaponEventRifleImpactDamageMod", "PrimePrice": 330, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/WeaponEventSlashDamageMod", "PrimePrice": 375, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/WeaponEventShotgunImpactDamageMod", "PrimePrice": 365, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Rifle/DualStat/ElectEventRifleMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/DualStat/ElectEventPistolMod", "PrimePrice": 300, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Mods/Pistol/WeaponEventSlashDamageMod", "PrimePrice": 375, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/VoidTrader/VTDetron", "PrimePrice": 500, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/LongGuns/CrpFreezeRay/Vandal/CrpFreezeRayVandalRifle", "PrimePrice": 475, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/ClanTech/Chemical/FlameThrowerWraith", "PrimePrice": 550, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/WraithMacheteWeapon", "PrimePrice": 410, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CrpHandRL/PrismaAngstrum", "PrimePrice": 475, "RegularPrice": 210000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/PrismaDualCleavers", "PrimePrice": 490, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/VoidTraderGorgon/VTGorgon", "PrimePrice": 600, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/VoidTrader/PrismaGrakata", "PrimePrice": 610, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrineerLeverActionRifle/PrismaGrinlokWeapon", "PrimePrice": 500, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/Melee/KickAndPunch/PrismaObex", "PrimePrice": 500, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/VoidTrader/PrismaSkana", "PrimePrice": 510, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/LongGuns/CorpusUMP/PrismaCorpusUMP", "PrimePrice": 400, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/Pistols/GrineerBulbousSMG/Prisma/PrismaTwinGremlinsWeapon", "PrimePrice": 500, "RegularPrice": 220000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Tenno/Archwing/Melee/VoidTraderArchsword/VTArchSwordWeapon", "PrimePrice": 550, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/ClanTech/Energy/VandalElectroProd", "PrimePrice": 410, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/LongGuns/CrpShockRifle/QuantaVandal", "PrimePrice": 450, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Corpus/LongGuns/Machinegun/SupraVandal", "PrimePrice": 500, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/Pistols/WraithSingleViper/WraithSingleViper", "PrimePrice": 400, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrineerSniperRifle/VulkarWraith", "PrimePrice": 450, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Weapons/Tenno/Pistols/ConclaveLeverPistol/ConclaveLeverPistol", "PrimePrice": 500, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/MeleeDangles/FireMeleeDangle", "PrimePrice": 100, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/BaroInarosPolearmSkin", "PrimePrice": 325, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/MeleeDangles/BaroInarosMeleeDangle", "PrimePrice": 250, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/MeleeDangles/InfestedMeleeDangle", "PrimePrice": 250, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/VTHalloweenDarkSword", "PrimePrice": 320, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/SummerSolstice/SummerSolsticeGorgon", "PrimePrice": 300, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/SummerSolstice/SummerIgnisSkin", "PrimePrice": 300, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/BaroArrow", "PrimePrice": 375, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/MeleeDangles/BaroMeleeDangle", "PrimePrice": 250, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/BaroScytheMacheteSkin", "PrimePrice": 375, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/ElixisOdonataSkin", "PrimePrice": 350, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/ElixisBallasSword", "PrimePrice": 350, "RegularPrice": 350000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/PrismaArrow", "PrimePrice": 350, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/VTRedeemerSkin", "PrimePrice": 325, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/ElixisSonicor", "PrimePrice": 380, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/ElixisTigris", "PrimePrice": 300, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/VTQuanta", "PrimePrice": 300, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/VoidTrader/ElixisOpticor", "PrimePrice": 325, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Halloween/HalloweenDread", "PrimePrice": 300, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/ImageBaroKiteer", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Seasonal/AvatarImageGlyphCookieKavat", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "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 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Pirate/HydroidAlternateSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/Seasonal/Halloween2019GrendelTreat", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/SuitCustomizations/ColourPickerKiteerItemA", "PrimePrice": 150, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/KazBaroCape", "PrimePrice": 325, "RegularPrice": 450000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Effects/BaroEphemeraA", "PrimePrice": 100, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/BaroCape2Scarf", "PrimePrice": 400, "RegularPrice": 350000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/BaroQuantumBadgeItem", "PrimePrice": 400, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/SolsticeBaroCape", "PrimePrice": 425, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/BaroCape", "PrimePrice": 500, "RegularPrice": 500000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageBaroIcon", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Magician/LimboImmortalSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Cowgirl/MesaImmortallSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Harlequin/MirageAlternateSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/BaroKubrowBadgeItem", "PrimePrice": 50, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/BaroKubrowSigil", "PrimePrice": 55, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/VTHornSkullScarf", "PrimePrice": 250, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Clan/PrismaLotusEmblem", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageBaroTwoIcon", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/PrismaLotusSigil", "PrimePrice": 55, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sigils/PrimeTraderSigil", "PrimePrice": 50, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/PrismaRazorScarf", "PrimePrice": 350, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Scarves/VTDinoSpikeScarf", "PrimePrice": 400, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageLowPolyKavat", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageLowPolyKubrow", "PrimePrice": 80, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Tengu/ZephyrAlternateSkin", "PrimePrice": 550, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Game/KubrowPet/Patterns/KubrowPetPatternPrimeTraderA", "PrimePrice": 150, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Camo/DesertDirigaSkin", "PrimePrice": 225, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Masks/KavatPetMask", "PrimePrice": 500, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Tails/KavatPetTail", "PrimePrice": 400, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Wings/KavatPetWings", "PrimePrice": 400, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Catbrows/Armor/CatbrowArmorVoidTraderA", "PrimePrice": 500, "RegularPrice": 275000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Kubrows/Armor/KubrowArmorBaro", "PrimePrice": 500, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Masks/BaroPetMask", "PrimePrice": 500, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Tails/BaroPetTail", "PrimePrice": 400, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Wings/BaroPetWings", "PrimePrice": 400, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/Types/StoreItems/Packages/KavatColorPackNexus", "PrimePrice": 200, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Wings/PrismaJetWings", "PrimePrice": 300, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Tails/PrismaFishTail", "PrimePrice": 200, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Masks/PrismaMechHeadMask", "PrimePrice": 175, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Kubrows/Armor/KubrowArmorPrisma", "PrimePrice": 400, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Sentinels/SentinelPowersuits/PrismaShadePowerSuit", "PrimePrice": 500, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Sentinels/Skins/DesertTaxonSkin", "PrimePrice": 200, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Catbrows/Armor/CatbrowArmorHalloweenA", "PrimePrice": 400, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/Types/StoreItems/Boosters/AffinityBooster3DayStoreItem", "PrimePrice": 450, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/Types/StoreItems/Boosters/CreditBooster3DayStoreItem", "PrimePrice": 350, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/Types/StoreItems/Boosters/ModDropChanceBooster3DayStoreItem", "PrimePrice": 500, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/Types/StoreItems/Boosters/ResourceAmount3DayStoreItem", "PrimePrice": 400, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Game/Projections/T4VoidProjectionPBronze", "PrimePrice": 50, "RegularPrice": 45000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Recipes/Components/CorruptedBombardBallBlueprint", "PrimePrice": 100, "RegularPrice": 50000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/CorruptedHeavyGunnerBall", "PrimePrice": 100, "RegularPrice": 40000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/OrbiterPictureFrameBaro", "PrimePrice": 100, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Restoratives/Consumable/AssassinBaitC", "PrimePrice": 200, "RegularPrice": 125000 },
 | 
				
			||||||
 | 
					    { "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 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationE", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropCleaningDroneBaro", "PrimePrice": 700, "RegularPrice": 500000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerBobbleHead", "PrimePrice": 70, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Hoverboard/HoverboardStickerBaroA", "PrimePrice": 75, "RegularPrice": 75000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/KavatBust", "PrimePrice": 220, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/KubrowBust", "PrimePrice": 220, "RegularPrice": 250000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDesertSkate", "PrimePrice": 125, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationD", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/ExcaliburArchwingBobbleHead", "PrimePrice": 90, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Accessories/BaroTiara", "PrimePrice": 525, "RegularPrice": 375000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Accessories/EarpieceBaroC", "PrimePrice": 500, "RegularPrice": 400000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Accessories/BaroMouthPieceA", "PrimePrice": 500, "RegularPrice": 400000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Accessories/BaroVisor", "PrimePrice": 525, "RegularPrice": 375000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Accessories/BaroHorn", "PrimePrice": 525, "RegularPrice": 375000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Accessories/EarpieceBaroA", "PrimePrice": 500, "RegularPrice": 400000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Operator/Accessories/EarpieceBaroB", "PrimePrice": 250, "RegularPrice": 200000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Game/QuartersWallpapers/BaroWallpaper", "PrimePrice": 250, "RegularPrice": 175000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Liset/InarosLisetSkin", "PrimePrice": 400, "RegularPrice": 300000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationA", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Liset/LisetInsectSkinInaros", "PrimePrice": 425, "RegularPrice": 320000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Liset/LisetInsectSkinPrimeTrader", "PrimePrice": 230, "RegularPrice": 375000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/ParazonPoster", "PrimePrice": 100, "RegularPrice": 125000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/KubrowKavatLowPolyPoster", "PrimePrice": 90, "RegularPrice": 110000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Liset/LisetSkinVoidTrader", "PrimePrice": 120, "RegularPrice": 150000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Liset/LisetBlueSkySkinPrimeTrader", "PrimePrice": 210, "RegularPrice": 450000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationF", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Liset/LisetBlueSkySkinInaros", "PrimePrice": 375, "RegularPrice": 340000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationG", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropOstRugBaro", "PrimePrice": 225, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Types/Items/ShipDecos/BaroKiTeerDecorationH", "PrimePrice": 100, "RegularPrice": 100000 },
 | 
				
			||||||
 | 
					    { "ItemType": "/Lotus/StoreItems/Upgrades/Skins/Liset/Gyroscope/LisetGyroscopeSkinPrimeTrader", "PrimePrice": 220, "RegularPrice": 400000 },
 | 
				
			||||||
 | 
					    { "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 }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  "allIfAny": [
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      "/Lotus/StoreItems/Upgrades/Skins/Operator/Leggings/LeggingsNovaEngineer",
 | 
				
			||||||
 | 
					      "/Lotus/StoreItems/Upgrades/Skins/Operator/BodySuits/BodySuitNovaEngineer",
 | 
				
			||||||
 | 
					      "/Lotus/StoreItems/Upgrades/Skins/Operator/Sleeves/SleevesNovaEngineer",
 | 
				
			||||||
 | 
					      "/Lotus/StoreItems/Upgrades/Skins/Operator/Hoods/HoodNovaEngineer"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										158
									
								
								static/fixed_responses/worldState/darvoDeals.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								static/fixed_responses/worldState/darvoDeals.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,158 @@
 | 
				
			|||||||
 | 
					[
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Archwing/DemolitionJetPack/DemolitionJetPack", "Discount": 60, "OriginalPrice": 275, "SalePrice": 110, "AmountTotal": 300, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Bard/Bard", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Ember/Ember", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Ember/Ember", "Discount": 60, "OriginalPrice": 225, "SalePrice": 90, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Magician/Magician", "Discount": 20, "OriginalPrice": 200, "SalePrice": 160, "AmountTotal": 200, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Magician/Magician", "Discount": 30, "OriginalPrice": 200, "SalePrice": 140, "AmountTotal": 200, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Magician/Magician", "Discount": 40, "OriginalPrice": 200, "SalePrice": 120, "AmountTotal": 200, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Magician/Magician", "Discount": 50, "OriginalPrice": 200, "SalePrice": 100, "AmountTotal": 200, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Sandman/Sandman", "Discount": 20, "OriginalPrice": 225, "SalePrice": 180, "AmountTotal": 100, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Sandman/Sandman", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Trapper/Trapper", "Discount": 40, "OriginalPrice": 300, "SalePrice": 180, "AmountTotal": 150, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Powersuits/Trapper/Trapper", "Discount": 50, "OriginalPrice": 300, "SalePrice": 150, "AmountTotal": 100, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Game/CatbrowPet/CatbrowGeneticSignature", "Discount": 20, "OriginalPrice": 5, "SalePrice": 4, "AmountTotal": 500, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Game/CatbrowPet/CatbrowGeneticSignature", "Discount": 30, "OriginalPrice": 5, "SalePrice": 3, "AmountTotal": 415, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Game/KubrowPet/Eggs/KubrowEgg", "Discount": 50, "OriginalPrice": 10, "SalePrice": 5, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Game/KubrowPet/Eggs/KubrowEgg", "Discount": 60, "OriginalPrice": 10, "SalePrice": 4, "AmountTotal": 100, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Forma", "Discount": 30, "OriginalPrice": 20, "SalePrice": 14, "AmountTotal": 150, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Forma", "Discount": 45, "OriginalPrice": 20, "SalePrice": 11, "AmountTotal": 150, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/OrokinReactor", "Discount": 40, "OriginalPrice": 20, "SalePrice": 12, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/BioComponent", "Discount": 10, "OriginalPrice": 10, "SalePrice": 9, "AmountTotal": 200, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/BioComponent", "Discount": 20, "OriginalPrice": 10, "SalePrice": 8, "AmountTotal": 165, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/BioComponent", "Discount": 30, "OriginalPrice": 10, "SalePrice": 7, "AmountTotal": 135, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/BioComponent", "Discount": 40, "OriginalPrice": 10, "SalePrice": 6, "AmountTotal": 100, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/ChemComponent", "Discount": 10, "OriginalPrice": 10, "SalePrice": 9, "AmountTotal": 200, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/ChemComponent", "Discount": 20, "OriginalPrice": 10, "SalePrice": 8, "AmountTotal": 165, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/ChemComponent", "Discount": 30, "OriginalPrice": 10, "SalePrice": 7, "AmountTotal": 135, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/EnergyComponent", "Discount": 10, "OriginalPrice": 10, "SalePrice": 9, "AmountTotal": 200, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/EnergyComponent", "Discount": 20, "OriginalPrice": 10, "SalePrice": 8, "AmountTotal": 165, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/EnergyComponent", "Discount": 30, "OriginalPrice": 10, "SalePrice": 7, "AmountTotal": 135, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Types/Items/Research/EnergyComponent", "Discount": 40, "OriginalPrice": 10, "SalePrice": 6, "AmountTotal": 100, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/AttackLensGreater", "Discount": 10, "OriginalPrice": 40, "SalePrice": 36, "AmountTotal": 150, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/AttackLensGreater", "Discount": 20, "OriginalPrice": 40, "SalePrice": 32, "AmountTotal": 125, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/AttackLensGreater", "Discount": 40, "OriginalPrice": 40, "SalePrice": 24, "AmountTotal": 75, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/DefenseLensGreater", "Discount": 10, "OriginalPrice": 40, "SalePrice": 36, "AmountTotal": 150, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/DefenseLensGreater", "Discount": 40, "OriginalPrice": 40, "SalePrice": 24, "AmountTotal": 75, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/DefenseLensGreater", "Discount": 50, "OriginalPrice": 40, "SalePrice": 20, "AmountTotal": 50, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/PowerLensGreater", "Discount": 50, "OriginalPrice": 40, "SalePrice": 20, "AmountTotal": 50, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 10, "OriginalPrice": 40, "SalePrice": 36, "AmountTotal": 150, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 20, "OriginalPrice": 40, "SalePrice": 32, "AmountTotal": 125, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 30, "OriginalPrice": 40, "SalePrice": 28, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 40, "OriginalPrice": 40, "SalePrice": 24, "AmountTotal": 75, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/TacticLensGreater", "Discount": 50, "OriginalPrice": 40, "SalePrice": 20, "AmountTotal": 50, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Upgrades/Focus/WardLensGreater", "Discount": 40, "OriginalPrice": 40, "SalePrice": 24, "AmountTotal": 75, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/CrpBow", "Discount": 20, "OriginalPrice": 235, "SalePrice": 188, "AmountTotal": 300, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/CrpBow", "Discount": 30, "OriginalPrice": 235, "SalePrice": 164, "AmountTotal": 250, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/CrpBow", "Discount": 50, "OriginalPrice": 235, "SalePrice": 117, "AmountTotal": 150, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Bow/Longbow/CrpBow", "Discount": 60, "OriginalPrice": 235, "SalePrice": 94, "AmountTotal": 100, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Melee/KickAndPunch/KickPunchWeapon", "Discount": 20, "OriginalPrice": 125, "SalePrice": 100, "AmountTotal": 100, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Melee/KickAndPunch/KickPunchWeapon", "Discount": 30, "OriginalPrice": 125, "SalePrice": 87, "AmountTotal": 90, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Melee/KickAndPunch/KickPunchWeapon", "Discount": 60, "OriginalPrice": 125, "SalePrice": 50, "AmountTotal": 65, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 40, "OriginalPrice": 175, "SalePrice": 105, "AmountTotal": 90, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 50, "OriginalPrice": 175, "SalePrice": 87, "AmountTotal": 80, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 60, "OriginalPrice": 175, "SalePrice": 70, "AmountTotal": 70, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Corpus/Pistols/CorpusMinigun/CorpusMinigun", "Discount": 70, "OriginalPrice": 175, "SalePrice": 52, "AmountTotal": 60, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/GrineerPistol/GrineerLightPistol", "Discount": 10, "OriginalPrice": 75, "SalePrice": 67, "AmountTotal": 100, "probability": 6 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/GrineerPistol/GrineerLightPistol", "Discount": 20, "OriginalPrice": 75, "SalePrice": 60, "AmountTotal": 100, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/GrineerPistol/GrineerLightPistol", "Discount": 30, "OriginalPrice": 75, "SalePrice": 52, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/BurstRifle/GrnBurstRifle", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 500, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/BurstRifle/GrnBurstRifle", "Discount": 40, "OriginalPrice": 225, "SalePrice": 135, "AmountTotal": 500, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/BurstRifle/GrnBurstRifle", "Discount": 60, "OriginalPrice": 225, "SalePrice": 90, "AmountTotal": 500, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrnSpark/GrnSparkRifle", "Discount": 20, "OriginalPrice": 150, "SalePrice": 120, "AmountTotal": 300, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrnSpark/GrnSparkRifle", "Discount": 30, "OriginalPrice": 150, "SalePrice": 105, "AmountTotal": 250, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/LongGuns/GrnSpark/GrnSparkRifle", "Discount": 50, "OriginalPrice": 150, "SalePrice": 75, "AmountTotal": 150, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 30, "OriginalPrice": 225, "SalePrice": 157, "AmountTotal": 200, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 40, "OriginalPrice": 225, "SalePrice": 135, "AmountTotal": 175, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 50, "OriginalPrice": 225, "SalePrice": 112, "AmountTotal": 150, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 60, "OriginalPrice": 225, "SalePrice": 90, "AmountTotal": 125, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Grineer/Melee/GrineerMachetteAndCleaver/DualCleaverWeapon", "Discount": 70, "OriginalPrice": 225, "SalePrice": 67, "AmountTotal": 100, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 10, "OriginalPrice": 150, "SalePrice": 135, "AmountTotal": 300, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 20, "OriginalPrice": 150, "SalePrice": 120, "AmountTotal": 270, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 30, "OriginalPrice": 150, "SalePrice": 105, "AmountTotal": 240, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 40, "OriginalPrice": 150, "SalePrice": 90, "AmountTotal": 205, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 60, "OriginalPrice": 150, "SalePrice": 60, "AmountTotal": 145, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Infested/Melee/Swords/Mire/MireSword", "Discount": 80, "OriginalPrice": 150, "SalePrice": 30, "AmountTotal": 80, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Akimbo/AkimboShotGun", "Discount": 20, "OriginalPrice": 225, "SalePrice": 180, "AmountTotal": 200, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Akimbo/AkimboShotGun", "Discount": 40, "OriginalPrice": 225, "SalePrice": 135, "AmountTotal": 165, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Akimbo/AkimboShotGun", "Discount": 50, "OriginalPrice": 225, "SalePrice": 112, "AmountTotal": 150, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Dagger/Dagger", "Discount": 30, "OriginalPrice": 75, "SalePrice": 52, "AmountTotal": 350, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Dagger/Dagger", "Discount": 40, "OriginalPrice": 75, "SalePrice": 45, "AmountTotal": 300, "probability": 7 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Dagger/Dagger", "Discount": 50, "OriginalPrice": 75, "SalePrice": 37, "AmountTotal": 250, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Dagger/Dagger", "Discount": 60, "OriginalPrice": 75, "SalePrice": 30, "AmountTotal": 200, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/DualShortSword/DualHeatSwords", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 200, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/DualShortSword/DualHeatSwords", "Discount": 70, "OriginalPrice": 175, "SalePrice": 52, "AmountTotal": 200, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Fist/Fist", "Discount": 10, "OriginalPrice": 125, "SalePrice": 112, "AmountTotal": 500, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Fist/Fist", "Discount": 20, "OriginalPrice": 125, "SalePrice": 100, "AmountTotal": 250, "probability": 6 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Gauntlet/Gauntlet", "Discount": 20, "OriginalPrice": 125, "SalePrice": 100, "AmountTotal": 100, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Gauntlet/Gauntlet", "Discount": 30, "OriginalPrice": 125, "SalePrice": 87, "AmountTotal": 125, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Gauntlet/Gauntlet", "Discount": 40, "OriginalPrice": 125, "SalePrice": 75, "AmountTotal": 150, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Glaives/Boomerang/BoomerangWeapon", "Discount": 30, "OriginalPrice": 150, "SalePrice": 105, "AmountTotal": 300, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Glaives/Boomerang/BoomerangWeapon", "Discount": 40, "OriginalPrice": 150, "SalePrice": 90, "AmountTotal": 250, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Glaives/Boomerang/BoomerangWeapon", "Discount": 50, "OriginalPrice": 150, "SalePrice": 75, "AmountTotal": 200, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 20, "OriginalPrice": 165, "SalePrice": 132, "AmountTotal": 300, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 30, "OriginalPrice": 165, "SalePrice": 115, "AmountTotal": 250, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 40, "OriginalPrice": 165, "SalePrice": 99, "AmountTotal": 200, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 50, "OriginalPrice": 165, "SalePrice": 82, "AmountTotal": 150, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Hammer/IceHammer/IceHammer", "Discount": 60, "OriginalPrice": 165, "SalePrice": 66, "AmountTotal": 100, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/LongSword/LongSword", "Discount": 50, "OriginalPrice": 150, "SalePrice": 75, "AmountTotal": 300, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/LongSword/LongSword", "Discount": 60, "OriginalPrice": 150, "SalePrice": 60, "AmountTotal": 265, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/LongSword/LongSword", "Discount": 70, "OriginalPrice": 150, "SalePrice": 45, "AmountTotal": 225, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/LongSword/LongSword", "Discount": 90, "OriginalPrice": 150, "SalePrice": 15, "AmountTotal": 150, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Scythe/EtherScytheWeapon", "Discount": 40, "OriginalPrice": 230, "SalePrice": 138, "AmountTotal": 250, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Scythe/EtherScytheWeapon", "Discount": 60, "OriginalPrice": 230, "SalePrice": 92, "AmountTotal": 150, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/GreatSword/TennoGreatSword", "Discount": 20, "OriginalPrice": 175, "SalePrice": 140, "AmountTotal": 100, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/GreatSword/TennoGreatSword", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 100, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/GreatSword/TennoGreatSword", "Discount": 40, "OriginalPrice": 175, "SalePrice": 105, "AmountTotal": 100, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/Swords/GreatSword/TennoGreatSword", "Discount": 90, "OriginalPrice": 175, "SalePrice": 17, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/SwordsAndBoards/MeleeContestWinnerOne/TennoSwordShield",
 | 
				
			||||||
 | 
					    "Discount": 30,
 | 
				
			||||||
 | 
					    "OriginalPrice": 150,
 | 
				
			||||||
 | 
					    "SalePrice": 105,
 | 
				
			||||||
 | 
					    "AmountTotal": 100,
 | 
				
			||||||
 | 
					    "probability": 1
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/SwordsAndBoards/MeleeContestWinnerOne/TennoSwordShield",
 | 
				
			||||||
 | 
					    "Discount": 70,
 | 
				
			||||||
 | 
					    "OriginalPrice": 150,
 | 
				
			||||||
 | 
					    "SalePrice": 45,
 | 
				
			||||||
 | 
					    "AmountTotal": 100,
 | 
				
			||||||
 | 
					    "probability": 1
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Melee/SwordsAndBoards/MeleeContestWinnerOne/TennoSwordShield",
 | 
				
			||||||
 | 
					    "Discount": 90,
 | 
				
			||||||
 | 
					    "OriginalPrice": 150,
 | 
				
			||||||
 | 
					    "SalePrice": 15,
 | 
				
			||||||
 | 
					    "AmountTotal": 100,
 | 
				
			||||||
 | 
					    "probability": 1
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/CrossBow", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 300, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/CrossBow", "Discount": 40, "OriginalPrice": 175, "SalePrice": 105, "AmountTotal": 250, "probability": 6 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/CrossBow", "Discount": 50, "OriginalPrice": 175, "SalePrice": 87, "AmountTotal": 200, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/CrossBow", "Discount": 60, "OriginalPrice": 175, "SalePrice": 70, "AmountTotal": 150, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/HandShotGun", "Discount": 20, "OriginalPrice": 190, "SalePrice": 152, "AmountTotal": 300, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/HandShotGun", "Discount": 30, "OriginalPrice": 190, "SalePrice": 133, "AmountTotal": 200, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/HandShotGun", "Discount": 40, "OriginalPrice": 190, "SalePrice": 114, "AmountTotal": 100, "probability": 5 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/RevolverPistol", "Discount": 20, "OriginalPrice": 190, "SalePrice": 152, "AmountTotal": 200, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/RevolverPistol", "Discount": 30, "OriginalPrice": 190, "SalePrice": 133, "AmountTotal": 150, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/RevolverPistol", "Discount": 40, "OriginalPrice": 190, "SalePrice": 114, "AmountTotal": 100, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistol/RevolverPistol", "Discount": 50, "OriginalPrice": 190, "SalePrice": 95, "AmountTotal": 50, "probability": 4 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 20, "OriginalPrice": 190, "SalePrice": 152, "AmountTotal": 300, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 30, "OriginalPrice": 190, "SalePrice": 133, "AmountTotal": 250, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 40, "OriginalPrice": 190, "SalePrice": 114, "AmountTotal": 200, "probability": 2 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 50, "OriginalPrice": 190, "SalePrice": 95, "AmountTotal": 150, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Pistols/TnBardPistol/TnBardPistolGun", "Discount": 60, "OriginalPrice": 190, "SalePrice": 76, "AmountTotal": 100, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Rifle/TennoSniperRifle", "Discount": 10, "OriginalPrice": 250, "SalePrice": 225, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Rifle/TennoSniperRifle", "Discount": 30, "OriginalPrice": 250, "SalePrice": 175, "AmountTotal": 100, "probability": 3 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Rifle/TennoSniperRifle", "Discount": 50, "OriginalPrice": 250, "SalePrice": 125, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Shotgun/QuadShotgun", "Discount": 50, "OriginalPrice": 225, "SalePrice": 112, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/Shotgun/QuadShotgun", "Discount": 70, "OriginalPrice": 225, "SalePrice": 67, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/ThrowingWeapons/Kunai", "Discount": 10, "OriginalPrice": 175, "SalePrice": 157, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/ThrowingWeapons/Kunai", "Discount": 20, "OriginalPrice": 175, "SalePrice": 140, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/ThrowingWeapons/Kunai", "Discount": 30, "OriginalPrice": 175, "SalePrice": 122, "AmountTotal": 100, "probability": 1 },
 | 
				
			||||||
 | 
					  { "StoreItem": "/Lotus/StoreItems/Weapons/Tenno/ThrowingWeapons/Kunai", "Discount": 40, "OriginalPrice": 175, "SalePrice": 105, "AmountTotal": 100, "probability": 2 }
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -98,9 +98,9 @@
 | 
				
			|||||||
                        <button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
 | 
					                        <button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
 | 
				
			||||||
                    </form>
 | 
					                    </form>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-md-3">
 | 
					                    <div class="col-md-3">
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="currency_RegularCredits"></h5>
 | 
					                            <h5 class="card-header" data-loc="currency_RegularCredits"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <p class="card-text" id="RegularCredits-owned"></p>
 | 
					                                <p class="card-text" id="RegularCredits-owned"></p>
 | 
				
			||||||
@ -112,7 +112,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-md-3">
 | 
					                    <div class="col-md-3">
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="currency_PremiumCredits"></h5>
 | 
					                            <h5 class="card-header" data-loc="currency_PremiumCredits"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <p class="card-text" id="PremiumCredits-owned"></p>
 | 
					                                <p class="card-text" id="PremiumCredits-owned"></p>
 | 
				
			||||||
@ -124,7 +124,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-md-3">
 | 
					                    <div class="col-md-3">
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="currency_FusionPoints"></h5>
 | 
					                            <h5 class="card-header" data-loc="currency_FusionPoints"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <p class="card-text" id="FusionPoints-owned"></p>
 | 
					                                <p class="card-text" id="FusionPoints-owned"></p>
 | 
				
			||||||
@ -136,7 +136,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-md-3">
 | 
					                    <div class="col-md-3">
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="currency_PrimeTokens"></h5>
 | 
					                            <h5 class="card-header" data-loc="currency_PrimeTokens"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <p class="card-text" id="PrimeTokens-owned"></p>
 | 
					                                <p class="card-text" id="PrimeTokens-owned"></p>
 | 
				
			||||||
@ -148,9 +148,9 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_suits"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_suits"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('Suits');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('Suits');return false;">
 | 
				
			||||||
@ -164,7 +164,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_longGuns"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_longGuns"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="handleModularSelection('LongGuns');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="handleModularSelection('LongGuns');return false;">
 | 
				
			||||||
@ -183,9 +183,9 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_pistols"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_pistols"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="handleModularSelection('Pistols');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="handleModularSelection('Pistols');return false;">
 | 
				
			||||||
@ -204,7 +204,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_melee"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_melee"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="handleModularSelection('Melee');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="handleModularSelection('Melee');return false;">
 | 
				
			||||||
@ -223,9 +223,9 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_spaceSuits"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_spaceSuits"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceSuits');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceSuits');return false;">
 | 
				
			||||||
@ -239,7 +239,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_spaceGuns"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_spaceGuns"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceGuns');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceGuns');return false;">
 | 
				
			||||||
@ -253,9 +253,9 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_spaceMelee"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_spaceMelee"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceMelee');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('SpaceMelee');return false;">
 | 
				
			||||||
@ -269,7 +269,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_mechSuits"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_mechSuits"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('MechSuits');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('MechSuits');return false;">
 | 
				
			||||||
@ -283,9 +283,9 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_sentinels"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_sentinels"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('Sentinels');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('Sentinels');return false;">
 | 
				
			||||||
@ -299,7 +299,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_moaPets"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_moaPets"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="handleModularSelection('MoaPets');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="handleModularSelection('MoaPets');return false;">
 | 
				
			||||||
@ -325,9 +325,9 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_kubrowPets"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_kubrowPets"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="handleModularSelection('KubrowPets');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="handleModularSelection('KubrowPets');return false;">
 | 
				
			||||||
@ -349,7 +349,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_sentinelWeapons"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_sentinelWeapons"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('SentinelWeapons');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('SentinelWeapons');return false;">
 | 
				
			||||||
@ -363,9 +363,9 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_operatorAmps"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_operatorAmps"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="handleModularSelection('OperatorAmps');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="handleModularSelection('OperatorAmps');return false;">
 | 
				
			||||||
@ -384,7 +384,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_hoverboards"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_hoverboards"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireModularEquipment('HoverBoards');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireModularEquipment('HoverBoards');return false;">
 | 
				
			||||||
@ -401,9 +401,9 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3 mb-3">
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_evolutionProgress"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_evolutionProgress"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEvolution();return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEvolution();return false;">
 | 
				
			||||||
@ -417,7 +417,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-lg-6">
 | 
					                    <div class="col-lg-6">
 | 
				
			||||||
                        <div class="card mb-3" style="height: 400px;">
 | 
					                        <div class="card" style="height: 400px;">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="inventory_Boosters"></h5>
 | 
					                            <h5 class="card-header" data-loc="inventory_Boosters"></h5>
 | 
				
			||||||
                            <div class="card-body overflow-auto">
 | 
					                            <div class="card-body overflow-auto">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireBoosters();return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireBoosters();return false;">
 | 
				
			||||||
@ -431,7 +431,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="card mb-3">
 | 
					                <div class="card">
 | 
				
			||||||
                    <h5 class="card-header" data-loc="general_bulkActions"></h5>
 | 
					                    <h5 class="card-header" data-loc="general_bulkActions"></h5>
 | 
				
			||||||
                    <div class="card-body">
 | 
					                    <div class="card-body">
 | 
				
			||||||
                        <div class="mb-2 d-flex flex-wrap gap-2">
 | 
					                        <div class="mb-2 d-flex flex-wrap gap-2">
 | 
				
			||||||
@ -458,7 +458,7 @@
 | 
				
			|||||||
            <div id="powersuit-route" data-route="~ /webui/powersuit/(.+)" data-title="Inventory | OpenWF WebUI">
 | 
					            <div id="powersuit-route" data-route="~ /webui/powersuit/(.+)" data-title="Inventory | OpenWF WebUI">
 | 
				
			||||||
                <h3 class="mb-0"></h3>
 | 
					                <h3 class="mb-0"></h3>
 | 
				
			||||||
                <p class="text-body-secondary"></p>
 | 
					                <p class="text-body-secondary"></p>
 | 
				
			||||||
                <div class="card mb-3">
 | 
					                <div class="card">
 | 
				
			||||||
                    <h5 class="card-header" data-loc="powersuit_archonShardsLabel"></h5>
 | 
					                    <h5 class="card-header" data-loc="powersuit_archonShardsLabel"></h5>
 | 
				
			||||||
                    <div class="card-body">
 | 
					                    <div class="card-body">
 | 
				
			||||||
                        <p>
 | 
					                        <p>
 | 
				
			||||||
@ -498,7 +498,7 @@
 | 
				
			|||||||
                                <a href="riven-tool/" target="_blank" data-loc="mods_fingerprintHelp"></a>
 | 
					                                <a href="riven-tool/" target="_blank" data-loc="mods_fingerprintHelp"></a>
 | 
				
			||||||
                            </form>
 | 
					                            </form>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="mods_rivens"></h5>
 | 
					                            <h5 class="card-header" data-loc="mods_rivens"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <table class="table table-hover w-100">
 | 
					                                <table class="table table-hover w-100">
 | 
				
			||||||
@ -521,7 +521,7 @@
 | 
				
			|||||||
                                </table>
 | 
					                                </table>
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="general_bulkActions"></h5>
 | 
					                            <h5 class="card-header" data-loc="general_bulkActions"></h5>
 | 
				
			||||||
                            <div class="card-body d-flex flex-wrap gap-2">
 | 
					                            <div class="card-body d-flex flex-wrap gap-2">
 | 
				
			||||||
                                <button class="btn btn-primary" onclick="doAddAllMods();" data-loc="mods_addMissingUnrankedMods"></button>
 | 
					                                <button class="btn btn-primary" onclick="doAddAllMods();" data-loc="mods_addMissingUnrankedMods"></button>
 | 
				
			||||||
@ -535,7 +535,7 @@
 | 
				
			|||||||
            <div data-route="/webui/quests" data-title="Quests | OpenWF WebUI">
 | 
					            <div data-route="/webui/quests" data-title="Quests | OpenWF WebUI">
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3">
 | 
				
			||||||
                    <div class="col-md-6">
 | 
					                    <div class="col-md-6">
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="quests_list"></h5>
 | 
					                            <h5 class="card-header" data-loc="quests_list"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('QuestKeys');return false;">
 | 
					                                <form class="input-group mb-3" onsubmit="doAcquireEquipment('QuestKeys');return false;">
 | 
				
			||||||
@ -549,7 +549,7 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="col-md-6">
 | 
					                    <div class="col-md-6">
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="general_bulkActions"></h5>
 | 
					                            <h5 class="card-header" data-loc="general_bulkActions"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <div class="d-flex flex-wrap gap-2">
 | 
					                                <div class="d-flex flex-wrap gap-2">
 | 
				
			||||||
@ -565,13 +565,13 @@
 | 
				
			|||||||
            <div data-route="/webui/cheats, /webui/settings" data-title="Cheats | OpenWF WebUI">
 | 
					            <div data-route="/webui/cheats, /webui/settings" data-title="Cheats | OpenWF WebUI">
 | 
				
			||||||
                <div class="row g-3">
 | 
					                <div class="row g-3">
 | 
				
			||||||
                    <div class="col-md-6">
 | 
					                    <div class="col-md-6">
 | 
				
			||||||
                        <div class="card mb-3">
 | 
					                        <div class="card">
 | 
				
			||||||
                            <h5 class="card-header" data-loc="cheats_server"></h5>
 | 
					                            <h5 class="card-header" data-loc="cheats_server"></h5>
 | 
				
			||||||
                            <div class="card-body">
 | 
					                            <div class="card-body">
 | 
				
			||||||
                                <div id="server-settings-no-perms" class="d-none">
 | 
					                                <div class="d-none config-admin-hide">
 | 
				
			||||||
                                    <p class="card-text" data-loc="cheats_administratorRequirement"></p>
 | 
					                                    <p class="card-text" data-loc="cheats_administratorRequirement"></p>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
                                <div id="server-settings" class="d-none">
 | 
					                                <div class="d-none config-admin-show config-form">
 | 
				
			||||||
                                    <div class="form-check">
 | 
					                                    <div class="form-check">
 | 
				
			||||||
                                        <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>
 | 
				
			||||||
@ -704,6 +704,14 @@
 | 
				
			|||||||
                                        <input class="form-check-input" type="checkbox" id="fullyStockedVendors" />
 | 
					                                        <input class="form-check-input" type="checkbox" id="fullyStockedVendors" />
 | 
				
			||||||
                                        <label class="form-check-label" for="fullyStockedVendors" data-loc="cheats_fullyStockedVendors"></label>
 | 
					                                        <label class="form-check-label" for="fullyStockedVendors" data-loc="cheats_fullyStockedVendors"></label>
 | 
				
			||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                    <div class="form-check">
 | 
				
			||||||
 | 
					                                        <input class="form-check-input" type="checkbox" id="baroAlwaysAvailable" />
 | 
				
			||||||
 | 
					                                        <label class="form-check-label" for="baroAlwaysAvailable" data-loc="cheats_baroAlwaysAvailable"></label>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                    <div class="form-check">
 | 
				
			||||||
 | 
					                                        <input class="form-check-input" type="checkbox" id="baroFullyStocked" />
 | 
				
			||||||
 | 
					                                        <label class="form-check-label" for="baroFullyStocked" data-loc="cheats_baroFullyStocked"></label>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
                                    <div class="form-check">
 | 
					                                    <div class="form-check">
 | 
				
			||||||
                                        <input class="form-check-input" type="checkbox" id="syndicateMissionsRepeatable" />
 | 
					                                        <input class="form-check-input" type="checkbox" id="syndicateMissionsRepeatable" />
 | 
				
			||||||
                                        <label class="form-check-label" for="syndicateMissionsRepeatable" data-loc="cheats_syndicateMissionsRepeatable"></label>
 | 
					                                        <label class="form-check-label" for="syndicateMissionsRepeatable" data-loc="cheats_syndicateMissionsRepeatable"></label>
 | 
				
			||||||
@ -760,14 +768,18 @@
 | 
				
			|||||||
                                        <input class="form-check-input" type="checkbox" id="unlockAllSimarisResearchEntries" />
 | 
					                                        <input class="form-check-input" type="checkbox" id="unlockAllSimarisResearchEntries" />
 | 
				
			||||||
                                        <label class="form-check-label" for="unlockAllSimarisResearchEntries" data-loc="cheats_unlockAllSimarisResearchEntries"></label>
 | 
					                                        <label class="form-check-label" for="unlockAllSimarisResearchEntries" data-loc="cheats_unlockAllSimarisResearchEntries"></label>
 | 
				
			||||||
                                    </div>
 | 
					                                    </div>
 | 
				
			||||||
                                    <form class="form-group mt-2" onsubmit="doSaveConfig('spoofMasteryRank'); return false;">
 | 
					                                    <div class="form-check">
 | 
				
			||||||
 | 
					                                        <input class="form-check-input" type="checkbox" id="disableDailyTribute" />
 | 
				
			||||||
 | 
					                                        <label class="form-check-label" for="disableDailyTribute" data-loc="cheats_disableDailyTribute"></label>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                    <form class="form-group mt-2" onsubmit="doSaveConfigInt('spoofMasteryRank'); return false;">
 | 
				
			||||||
                                        <label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
 | 
					                                        <label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
 | 
				
			||||||
                                        <div class="input-group">
 | 
					                                        <div class="input-group">
 | 
				
			||||||
                                            <input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" />
 | 
					                                            <input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" />
 | 
				
			||||||
                                            <button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
 | 
					                                            <button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
 | 
				
			||||||
                                        </div>
 | 
					                                        </div>
 | 
				
			||||||
                                    </form>
 | 
					                                    </form>
 | 
				
			||||||
                                    <form class="form-group mt-2" onsubmit="doSaveConfig('nightwaveStandingMultiplier'); return false;">
 | 
					                                    <form class="form-group mt-2" onsubmit="doSaveConfigInt('nightwaveStandingMultiplier'); return false;">
 | 
				
			||||||
                                        <label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label>
 | 
					                                        <label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label>
 | 
				
			||||||
                                        <div class="input-group">
 | 
					                                        <div class="input-group">
 | 
				
			||||||
                                            <input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" value="1" />
 | 
					                                            <input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" value="1" />
 | 
				
			||||||
@ -786,6 +798,7 @@
 | 
				
			|||||||
                                    <button class="btn btn-primary" onclick="debounce(doUnlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(doUnlockAllMissions);" data-loc="cheats_unlockAllMissions"></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="doIntrinsicsUnlockAll();" data-loc="cheats_intrinsicsUnlockAll"></button>
 | 
					                                    <button class="btn btn-primary" onclick="doIntrinsicsUnlockAll();" data-loc="cheats_intrinsicsUnlockAll"></button>
 | 
				
			||||||
                                    <button class="btn btn-primary" onclick="debounce(doMaxPlexus);" data-loc="inventory_maxPlexus"></button>
 | 
					                                    <button class="btn btn-primary" onclick="debounce(doMaxPlexus);" data-loc="inventory_maxPlexus"></button>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
@ -798,13 +811,118 @@
 | 
				
			|||||||
                                </form>
 | 
					                                </form>
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div class="card d-none config-admin-show config-form">
 | 
				
			||||||
 | 
					                            <h5 class="card-header" data-loc="worldState"></h5>
 | 
				
			||||||
 | 
					                            <div class="card-body">
 | 
				
			||||||
 | 
					                                <div class="form-check">
 | 
				
			||||||
 | 
					                                    <input class="form-check-input" type="checkbox" id="worldState.creditBoost" />
 | 
				
			||||||
 | 
					                                    <label class="form-check-label" for="worldState.creditBoost" data-loc="worldState_creditBoost"></label>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-check">
 | 
				
			||||||
 | 
					                                    <input class="form-check-input" type="checkbox" id="worldState.affinityBoost" />
 | 
				
			||||||
 | 
					                                    <label class="form-check-label" for="worldState.affinityBoost" data-loc="worldState_affinityBoost"></label>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-check">
 | 
				
			||||||
 | 
					                                    <input class="form-check-input" type="checkbox" id="worldState.resourceBoost" />
 | 
				
			||||||
 | 
					                                    <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>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="changeSyndicate" data-loc="worldState_galleonOfGhouls"></label>
 | 
				
			||||||
 | 
					                                    <select class="form-control" id="worldState.galleonOfGhouls">
 | 
				
			||||||
 | 
					                                        <option value="0" data-loc="disabled"></option>
 | 
				
			||||||
 | 
					                                        <option value="1" data-loc="worldState_we1"></option>
 | 
				
			||||||
 | 
					                                        <option value="2" data-loc="worldState_we2"></option>
 | 
				
			||||||
 | 
					                                        <option value="3" data-loc="worldState_we3"></option>
 | 
				
			||||||
 | 
					                                    </select>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="changeSyndicate" data-loc="worldState_eidolonOverride"></label>
 | 
				
			||||||
 | 
					                                    <select class="form-control" id="worldState.eidolonOverride">
 | 
				
			||||||
 | 
					                                        <option value="" data-loc="disabled"></option>
 | 
				
			||||||
 | 
					                                        <option value="day" data-loc="worldState_day"></option>
 | 
				
			||||||
 | 
					                                        <option value="night" data-loc="worldState_night"></option>
 | 
				
			||||||
 | 
					                                    </select>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="changeSyndicate" data-loc="worldState_vallisOverride"></label>
 | 
				
			||||||
 | 
					                                    <select class="form-control" id="worldState.vallisOverride">
 | 
				
			||||||
 | 
					                                        <option value="" data-loc="disabled"></option>
 | 
				
			||||||
 | 
					                                        <option value="warm" data-loc="worldState_warm"></option>
 | 
				
			||||||
 | 
					                                        <option value="cold" data-loc="worldState_cold"></option>
 | 
				
			||||||
 | 
					                                    </select>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="changeSyndicate" data-loc="worldState_duviriOverride"></label>
 | 
				
			||||||
 | 
					                                    <select class="form-control" id="worldState.duviriOverride">
 | 
				
			||||||
 | 
					                                        <option value="" data-loc="disabled"></option>
 | 
				
			||||||
 | 
					                                        <option value="joy" data-loc="worldState_joy"></option>
 | 
				
			||||||
 | 
					                                        <option value="anger" data-loc="worldState_anger"></option>
 | 
				
			||||||
 | 
					                                        <option value="envy" data-loc="worldState_envy"></option>
 | 
				
			||||||
 | 
					                                        <option value="sorrow" data-loc="worldState_sorrow"></option>
 | 
				
			||||||
 | 
					                                        <option value="fear" data-loc="worldState_fear"></option>
 | 
				
			||||||
 | 
					                                    </select>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="changeSyndicate" data-loc="worldState_nightwaveOverride"></label>
 | 
				
			||||||
 | 
					                                    <select class="form-control" id="worldState.nightwaveOverride">
 | 
				
			||||||
 | 
					                                        <option value="" data-loc="disabled"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission13Syndicate" data-loc="worldState_RadioLegionIntermission13Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission12Syndicate" data-loc="worldState_RadioLegionIntermission12Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission11Syndicate" data-loc="worldState_RadioLegionIntermission11Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission10Syndicate" data-loc="worldState_RadioLegionIntermission10Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission9Syndicate" data-loc="worldState_RadioLegionIntermission9Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission8Syndicate" data-loc="worldState_RadioLegionIntermission8Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission7Syndicate" data-loc="worldState_RadioLegionIntermission7Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission6Syndicate" data-loc="worldState_RadioLegionIntermission6Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission5Syndicate" data-loc="worldState_RadioLegionIntermission5Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission4Syndicate" data-loc="worldState_RadioLegionIntermission4Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission3Syndicate" data-loc="worldState_RadioLegionIntermission3Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegion3Syndicate" data-loc="worldState_RadioLegion3Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermission2Syndicate" data-loc="worldState_RadioLegionIntermission2Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegion2Syndicate" data-loc="worldState_RadioLegion2Syndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionIntermissionSyndicate" data-loc="worldState_RadioLegionIntermissionSyndicate"></option>
 | 
				
			||||||
 | 
					                                        <option value="RadioLegionSyndicate" data-loc="worldState_RadioLegionSyndicate"></option>
 | 
				
			||||||
 | 
					                                    </select>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="form-group mt-2">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="changeSyndicate" data-loc="worldState_fissures"></label>
 | 
				
			||||||
 | 
					                                    <select class="form-control" id="worldState.allTheFissures">
 | 
				
			||||||
 | 
					                                        <option value="" data-loc="normal"></option>
 | 
				
			||||||
 | 
					                                        <option value="normal" data-loc="worldState_allAtOnceNormal"></option>
 | 
				
			||||||
 | 
					                                        <option value="hard" data-loc="worldState_allAtOnceSteelPath"></option>
 | 
				
			||||||
 | 
					                                    </select>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <form class="form-group mt-2" onsubmit="doSaveConfigStringArray('worldState.circuitGameModes'); return false;">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="worldState.circuitGameModes" data-loc="worldState_theCircuitOverride"></label>
 | 
				
			||||||
 | 
					                                    <div class="input-group">
 | 
				
			||||||
 | 
					                                        <input id="worldState.circuitGameModes" type="text" class="form-control tags-input" list="datalist-circuitGameModes" />
 | 
				
			||||||
 | 
					                                        <button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                </form>
 | 
				
			||||||
 | 
					                                <form class="form-group mt-2" onsubmit="doSaveConfigFloat('worldState.darvoStockMultiplier'); return false;">
 | 
				
			||||||
 | 
					                                    <label class="form-label" for="worldState.circuitGameModes" data-loc="worldState_darvoStockMultiplier"></label>
 | 
				
			||||||
 | 
					                                    <div class="input-group">
 | 
				
			||||||
 | 
					                                        <input id="worldState.darvoStockMultiplier" class="form-control" type="number" step="0.01" placeholder="1" />
 | 
				
			||||||
 | 
					                                        <button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                </form>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div data-route="/webui/import" data-title="Import | OpenWF WebUI">
 | 
					            <div data-route="/webui/import" data-title="Import | OpenWF WebUI">
 | 
				
			||||||
                <p data-loc="import_importNote"></p>
 | 
					                <p data-loc="import_importNote"></p>
 | 
				
			||||||
                <textarea class="form-control" id="import-inventory"></textarea>
 | 
					                <textarea class="form-control" id="import-inventory" style="height: calc(100vh - 300px)"></textarea>
 | 
				
			||||||
                <button class="btn btn-primary mt-3" onclick="doImport();" data-loc="import_submit"></button>
 | 
					                <button class="btn btn-primary mt-3" onclick="doImport();" data-loc="import_submit"></button>
 | 
				
			||||||
 | 
					                <p class="mt-3 mb-1" data-loc="import_samples"></p>
 | 
				
			||||||
 | 
					                <ul>
 | 
				
			||||||
 | 
					                    <li><a href="#" onclick="event.preventDefault();setImportSample('maxFocus');" data-loc="import_samples_maxFocus"></a></li>
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="toast-container position-fixed bottom-0 end-0 p-3"></div>
 | 
					        <div class="toast-container position-fixed bottom-0 end-0 p-3"></div>
 | 
				
			||||||
@ -823,10 +941,7 @@
 | 
				
			|||||||
    <datalist id="datalist-KubrowPets"></datalist>
 | 
					    <datalist id="datalist-KubrowPets"></datalist>
 | 
				
			||||||
    <datalist id="datalist-QuestKeys"></datalist>
 | 
					    <datalist id="datalist-QuestKeys"></datalist>
 | 
				
			||||||
    <datalist id="datalist-miscitems"></datalist>
 | 
					    <datalist id="datalist-miscitems"></datalist>
 | 
				
			||||||
    <datalist id="datalist-mods">
 | 
					    <datalist id="datalist-mods"></datalist>
 | 
				
			||||||
        <option data-key="/Lotus/Upgrades/Mods/Fusers/LegendaryModFuser" value="Legendary Core"></option>
 | 
					 | 
				
			||||||
        <option data-key="/Lotus/Upgrades/CosmeticEnhancers/Peculiars/CyoteMod" value="Traumatic Peculiar"></option>
 | 
					 | 
				
			||||||
    </datalist>
 | 
					 | 
				
			||||||
    <datalist id="datalist-archonCrystalUpgrades"></datalist>
 | 
					    <datalist id="datalist-archonCrystalUpgrades"></datalist>
 | 
				
			||||||
    <datalist id="datalist-OperatorAmps"></datalist>
 | 
					    <datalist id="datalist-OperatorAmps"></datalist>
 | 
				
			||||||
    <datalist id="datalist-EvolutionProgress"></datalist>
 | 
					    <datalist id="datalist-EvolutionProgress"></datalist>
 | 
				
			||||||
@ -858,6 +973,15 @@
 | 
				
			|||||||
    <datalist id="datalist-ModularParts-KUBROW_ANTIGEN"></datalist>
 | 
					    <datalist id="datalist-ModularParts-KUBROW_ANTIGEN"></datalist>
 | 
				
			||||||
    <datalist id="datalist-ModularParts-KUBROW_MUTAGEN"></datalist>
 | 
					    <datalist id="datalist-ModularParts-KUBROW_MUTAGEN"></datalist>
 | 
				
			||||||
    <datalist id="datalist-Boosters"></datalist>
 | 
					    <datalist id="datalist-Boosters"></datalist>
 | 
				
			||||||
 | 
					    <datalist id="datalist-circuitGameModes">
 | 
				
			||||||
 | 
					        <option>Survival</option>
 | 
				
			||||||
 | 
					        <option>VoidFlood</option>
 | 
				
			||||||
 | 
					        <option>Excavation</option>
 | 
				
			||||||
 | 
					        <option>Defense</option>
 | 
				
			||||||
 | 
					        <option>Exterminate</option>
 | 
				
			||||||
 | 
					        <option>Assassination</option>
 | 
				
			||||||
 | 
					        <option>Alchemy</option>
 | 
				
			||||||
 | 
					    </datalist>
 | 
				
			||||||
    <script src="/webui/libs/jquery-3.6.0.min.js"></script>
 | 
					    <script src="/webui/libs/jquery-3.6.0.min.js"></script>
 | 
				
			||||||
    <script src="/webui/libs/whirlpool-js.min.js"></script>
 | 
					    <script src="/webui/libs/whirlpool-js.min.js"></script>
 | 
				
			||||||
    <script src="/webui/libs/single.js"></script>
 | 
					    <script src="/webui/libs/single.js"></script>
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
let auth_pending = false,
 | 
					let auth_pending = false,
 | 
				
			||||||
    did_initial_auth = false,
 | 
					    did_initial_auth = false,
 | 
				
			||||||
    ws_is_open = false;
 | 
					    ws_is_open = false,
 | 
				
			||||||
 | 
					    wsid = 0;
 | 
				
			||||||
const sendAuth = isRegister => {
 | 
					const sendAuth = isRegister => {
 | 
				
			||||||
    if (ws_is_open && localStorage.getItem("email") && localStorage.getItem("password")) {
 | 
					    if (ws_is_open && localStorage.getItem("email") && localStorage.getItem("password")) {
 | 
				
			||||||
        auth_pending = true;
 | 
					        auth_pending = true;
 | 
				
			||||||
@ -34,6 +35,9 @@ function openWebSocket() {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
    window.ws.onmessage = e => {
 | 
					    window.ws.onmessage = e => {
 | 
				
			||||||
        const msg = JSON.parse(e.data);
 | 
					        const msg = JSON.parse(e.data);
 | 
				
			||||||
 | 
					        if ("wsid" in msg) {
 | 
				
			||||||
 | 
					            wsid = msg.wsid;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if ("reload" in msg) {
 | 
					        if ("reload" in msg) {
 | 
				
			||||||
            setTimeout(() => {
 | 
					            setTimeout(() => {
 | 
				
			||||||
                getWebSocket().then(() => {
 | 
					                getWebSocket().then(() => {
 | 
				
			||||||
@ -248,7 +252,8 @@ const permanentEvolutionWeapons = new Set([
 | 
				
			|||||||
    "/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon",
 | 
					    "/Lotus/Weapons/Tenno/Zariman/Melee/Tonfas/ZarimanTonfaWeapon",
 | 
				
			||||||
    "/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol",
 | 
					    "/Lotus/Weapons/Tenno/Zariman/Pistols/HeavyPistol/ZarimanHeavyPistol",
 | 
				
			||||||
    "/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon",
 | 
					    "/Lotus/Weapons/Thanotech/EntFistIncarnon/EntFistIncarnon",
 | 
				
			||||||
    "/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon"
 | 
					    "/Lotus/Weapons/Thanotech/EntratiWristGun/EntratiWristGunWeapon",
 | 
				
			||||||
 | 
					    "/Lotus/Weapons/Tenno/Zariman/Melee/HeavyScythe/ZarimanHeavyScythe/ZarimanHeavyScytheWeapon"
 | 
				
			||||||
]);
 | 
					]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let uniqueLevelCaps = {};
 | 
					let uniqueLevelCaps = {};
 | 
				
			||||||
@ -1449,6 +1454,11 @@ function addMissingEquipment(categories) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function addMissingHelminthRecipes() {
 | 
				
			||||||
 | 
					    await revalidateAuthz();
 | 
				
			||||||
 | 
					    await fetch("/custom/addMissingHelminthBlueprints?" + window.authz);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function addMissingEvolutionProgress() {
 | 
					function addMissingEvolutionProgress() {
 | 
				
			||||||
    const requests = [];
 | 
					    const requests = [];
 | 
				
			||||||
    document.querySelectorAll("#datalist-EvolutionProgress option").forEach(elm => {
 | 
					    document.querySelectorAll("#datalist-EvolutionProgress option").forEach(elm => {
 | 
				
			||||||
@ -1841,16 +1851,28 @@ function doAcquireMod() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const uiConfigs = [...$("#server-settings input[id]")].map(x => x.id);
 | 
					const uiConfigs = [...$(".config-form input[id], .config-form select[id]")].map(x => x.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for (const id of uiConfigs) {
 | 
					for (const id of uiConfigs) {
 | 
				
			||||||
    const elm = document.getElementById(id);
 | 
					    const elm = document.getElementById(id);
 | 
				
			||||||
    if (elm.type == "checkbox") {
 | 
					    if (elm.tagName == "SELECT") {
 | 
				
			||||||
 | 
					        elm.onchange = function () {
 | 
				
			||||||
 | 
					            let value = this.value;
 | 
				
			||||||
 | 
					            if (!isNaN(parseInt(value))) {
 | 
				
			||||||
 | 
					                value = parseInt(value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $.post({
 | 
				
			||||||
 | 
					                url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
 | 
				
			||||||
 | 
					                contentType: "application/json",
 | 
				
			||||||
 | 
					                data: JSON.stringify({ [id]: value })
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    } else if (elm.type == "checkbox") {
 | 
				
			||||||
        elm.onchange = function () {
 | 
					        elm.onchange = function () {
 | 
				
			||||||
            $.post({
 | 
					            $.post({
 | 
				
			||||||
                url: "/custom/config?" + window.authz,
 | 
					                url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
 | 
				
			||||||
                contentType: "application/json",
 | 
					                contentType: "application/json",
 | 
				
			||||||
                data: JSON.stringify({ key: id, value: this.checked })
 | 
					                data: JSON.stringify({ [id]: this.checked })
 | 
				
			||||||
            }).then(() => {
 | 
					            }).then(() => {
 | 
				
			||||||
                if (["infiniteCredits", "infinitePlatinum", "infiniteEndo", "infiniteRegalAya"].indexOf(id) != -1) {
 | 
					                if (["infiniteCredits", "infinitePlatinum", "infiniteEndo", "infiniteRegalAya"].indexOf(id) != -1) {
 | 
				
			||||||
                    updateInventory();
 | 
					                    updateInventory();
 | 
				
			||||||
@ -1860,12 +1882,37 @@ for (const id of uiConfigs) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function doSaveConfig(id) {
 | 
					function doSaveConfigInt(id) {
 | 
				
			||||||
    const elm = document.getElementById(id);
 | 
					 | 
				
			||||||
    $.post({
 | 
					    $.post({
 | 
				
			||||||
        url: "/custom/config?" + window.authz,
 | 
					        url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
 | 
				
			||||||
        contentType: "application/json",
 | 
					        contentType: "application/json",
 | 
				
			||||||
        data: JSON.stringify({ key: id, value: parseInt(elm.value) })
 | 
					        data: JSON.stringify({
 | 
				
			||||||
 | 
					            [id]: parseInt(document.getElementById(id).value)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function doSaveConfigFloat(id) {
 | 
				
			||||||
 | 
					    $.post({
 | 
				
			||||||
 | 
					        url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
 | 
				
			||||||
 | 
					        contentType: "application/json",
 | 
				
			||||||
 | 
					        data: JSON.stringify({
 | 
				
			||||||
 | 
					            [id]: parseFloat(document.getElementById(id).value)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function doSaveConfigStringArray(id) {
 | 
				
			||||||
 | 
					    $.post({
 | 
				
			||||||
 | 
					        url: "/custom/setConfig?" + window.authz + "&wsid=" + wsid,
 | 
				
			||||||
 | 
					        contentType: "application/json",
 | 
				
			||||||
 | 
					        data: JSON.stringify({
 | 
				
			||||||
 | 
					            [id]: document
 | 
				
			||||||
 | 
					                .getElementById(id)
 | 
				
			||||||
 | 
					                .getAttribute("data-tags-value")
 | 
				
			||||||
 | 
					                .split(", ")
 | 
				
			||||||
 | 
					                .filter(x => x)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1876,26 +1923,32 @@ single.getRoute("/webui/cheats").on("beforeload", function () {
 | 
				
			|||||||
    interval = setInterval(() => {
 | 
					    interval = setInterval(() => {
 | 
				
			||||||
        if (window.authz) {
 | 
					        if (window.authz) {
 | 
				
			||||||
            clearInterval(interval);
 | 
					            clearInterval(interval);
 | 
				
			||||||
            fetch("/custom/config?" + window.authz).then(async res => {
 | 
					            $.post({
 | 
				
			||||||
                if (res.status == 200) {
 | 
					                url: "/custom/getConfig?" + window.authz,
 | 
				
			||||||
 | 
					                contentType: "application/json",
 | 
				
			||||||
 | 
					                data: JSON.stringify(uiConfigs)
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					                .done(json => {
 | 
				
			||||||
                    //window.is_admin = true;
 | 
					                    //window.is_admin = true;
 | 
				
			||||||
                    $("#server-settings-no-perms").addClass("d-none");
 | 
					                    $(".config-admin-hide").addClass("d-none");
 | 
				
			||||||
                    $("#server-settings").removeClass("d-none");
 | 
					                    $(".config-admin-show").removeClass("d-none");
 | 
				
			||||||
                    res.json().then(json =>
 | 
					                    Object.entries(json).forEach(entry => {
 | 
				
			||||||
                        Object.entries(json).forEach(entry => {
 | 
					                        const [key, value] = entry;
 | 
				
			||||||
                            const [key, value] = entry;
 | 
					                        var x = document.getElementById(`${key}`);
 | 
				
			||||||
                            var x = document.getElementById(`${key}`);
 | 
					                        if (x != null) {
 | 
				
			||||||
                            if (x != null) {
 | 
					                            if (x.type == "checkbox") {
 | 
				
			||||||
                                if (x.type == "checkbox") {
 | 
					                                x.checked = value;
 | 
				
			||||||
                                    x.checked = value;
 | 
					                            } else if (x.classList.contains("tags-input")) {
 | 
				
			||||||
                                } else if (x.type == "number") {
 | 
					                                x.value = value.join(", ");
 | 
				
			||||||
                                    x.setAttribute("value", `${value}`);
 | 
					                                x.oninput();
 | 
				
			||||||
                                }
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                x.value = value;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        })
 | 
					                        }
 | 
				
			||||||
                    );
 | 
					                    });
 | 
				
			||||||
                } else {
 | 
					                })
 | 
				
			||||||
                    if ((await res.text()) == "Log-in expired") {
 | 
					                .fail(res => {
 | 
				
			||||||
 | 
					                    if (res.responseText == "Log-in expired") {
 | 
				
			||||||
                        revalidateAuthz().then(() => {
 | 
					                        revalidateAuthz().then(() => {
 | 
				
			||||||
                            if (single.getCurrentPath() == "/webui/cheats") {
 | 
					                            if (single.getCurrentPath() == "/webui/cheats") {
 | 
				
			||||||
                                single.loadRoute("/webui/cheats");
 | 
					                                single.loadRoute("/webui/cheats");
 | 
				
			||||||
@ -1903,11 +1956,10 @@ single.getRoute("/webui/cheats").on("beforeload", function () {
 | 
				
			|||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        //window.is_admin = false;
 | 
					                        //window.is_admin = false;
 | 
				
			||||||
                        $("#server-settings-no-perms").removeClass("d-none");
 | 
					                        $(".config-admin-hide").removeClass("d-none");
 | 
				
			||||||
                        $("#server-settings").addClass("d-none");
 | 
					                        $(".config-admin-show").addClass("d-none");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                });
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }, 10);
 | 
					    }, 10);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
@ -2269,14 +2321,13 @@ function doAcquireBoosters() {
 | 
				
			|||||||
    const ExpiryDate = Date.now() / 1000 + 3 * 24 * 60 * 60; // default 3 days
 | 
					    const ExpiryDate = Date.now() / 1000 + 3 * 24 * 60 * 60; // default 3 days
 | 
				
			||||||
    setBooster(uniqueName, ExpiryDate, () => {
 | 
					    setBooster(uniqueName, ExpiryDate, () => {
 | 
				
			||||||
        $("#acquire-type-Boosters").val("");
 | 
					        $("#acquire-type-Boosters").val("");
 | 
				
			||||||
        updateInventory();
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function doChangeBoosterExpiry(ItemType, ExpiryDateInput) {
 | 
					function doChangeBoosterExpiry(ItemType, ExpiryDateInput) {
 | 
				
			||||||
    console.log("Changing booster expiry for", ItemType, "to", ExpiryDateInput.value);
 | 
					    console.log("Changing booster expiry for", ItemType, "to", ExpiryDateInput.value);
 | 
				
			||||||
    // cast local datetime string to unix timestamp
 | 
					    // cast local datetime string to unix timestamp
 | 
				
			||||||
    const ExpiryDate = new Date(ExpiryDateInput.value).getTime() / 1000;
 | 
					    const ExpiryDate = Math.trunc(new Date(ExpiryDateInput.value).getTime() / 1000);
 | 
				
			||||||
    if (isNaN(ExpiryDate)) {
 | 
					    if (isNaN(ExpiryDate)) {
 | 
				
			||||||
        ExpiryDateInput.addClass("is-invalid").focus();
 | 
					        ExpiryDateInput.addClass("is-invalid").focus();
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
@ -2339,3 +2390,262 @@ async function doUnlockAllMissions() {
 | 
				
			|||||||
    await fetch("/custom/completeAllMissions?" + window.authz);
 | 
					    await fetch("/custom/completeAllMissions?" + window.authz);
 | 
				
			||||||
    updateInventory();
 | 
					    updateInventory();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const importSamples = {
 | 
				
			||||||
 | 
					    maxFocus: {
 | 
				
			||||||
 | 
					        FocusUpgrades: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/AttackFocusAbility"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Stats/MoreAmmoFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Residual/PowerSnapFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Residual/PhysicalDamageFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Active/CloakAttackChargeFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Stats/RegenAmmoFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/TacticFocusAbility"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/WardFocusAbility"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/DefenseFocusAbility"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/PowerFocusAbility"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Active/KnockdownImmunityFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Active/UnairuWispFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Active/SunderingDissipationUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Active/MagneticExtensionUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Active/MagneticFieldFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Residual/ArmourBuffFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Active/ClearStaticOnKillFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Residual/SecondChanceDamageBuffFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Residual/SecondChanceFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Ward/Active/InvulnerableReturnFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Active/ConsecutivePowerUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Active/AttackEfficiencyFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Active/GhostlyTouchUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Active/GhostWaveUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Attack/Active/ConsecutiveEfficienyUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Active/ProjectionStretchUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Active/ProjectionExecutionUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Active/FinisherTransferenceUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Active/ComboAmpDamageFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Residual/MeleeComboFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Residual/MeleeXpFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Active/LiftHitWaveUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Active/LiftHitDamageUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Stats/MoveSpeedFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Tactic/Active/SlamComboFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Active/PowerFieldFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Active/DisarmedEnergyUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Stats/EnergyPoolFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Residual/EnergyOverTimeFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Active/BlastSlowFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Stats/EnergyRestoreFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Residual/FreeAbilityCastsFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Active/DisarmingProjectionUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Residual/SlowHeadshotDamageFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Power/Active/DashBubbleFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Stats/HealthRegenFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Residual/RadialXpFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Active/DefenseShieldFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Active/CloakHealFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Active/DefenseShieldBreakFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Active/DashImmunityFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Residual/InstantReviveFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Active/SonicDissipationUpgrade",
 | 
				
			||||||
 | 
					                Level: 3
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Stats/HealthMaxFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 3,
 | 
				
			||||||
 | 
					                IsUniversal: true
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ItemType: "/Lotus/Upgrades/Focus/Defense/Active/CloakHealOthersFocusUpgrade",
 | 
				
			||||||
 | 
					                Level: 2
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					function setImportSample(key) {
 | 
				
			||||||
 | 
					    $("#import-inventory").val(JSON.stringify(importSamples[key], null, 2));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					document.querySelectorAll(".tags-input").forEach(input => {
 | 
				
			||||||
 | 
					    const datalist = document.getElementById(input.getAttribute("list"));
 | 
				
			||||||
 | 
					    const options = [...datalist.querySelectorAll("option")].map(x => x.textContent);
 | 
				
			||||||
 | 
					    input.oninput = function () {
 | 
				
			||||||
 | 
					        const value = [];
 | 
				
			||||||
 | 
					        for (const tag of this.value.split(",")) {
 | 
				
			||||||
 | 
					            const index = options.map(x => x.toLowerCase()).indexOf(tag.trim().toLowerCase());
 | 
				
			||||||
 | 
					            if (index != -1) {
 | 
				
			||||||
 | 
					                value.push(options[index]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.setAttribute("data-tags-value", value.join(", "));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        datalist.innerHTML = "";
 | 
				
			||||||
 | 
					        for (const option of options) {
 | 
				
			||||||
 | 
					            const elm = document.createElement("option");
 | 
				
			||||||
 | 
					            elm.textContent = [...value, option, ""].join(", ");
 | 
				
			||||||
 | 
					            datalist.appendChild(elm);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    input.oninput();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
// German translation by Animan8000
 | 
					// German translation by Animan8000
 | 
				
			||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `Hinweis: Änderungen, die hier vorgenommen werden, werden erst im Spiel angewendet, sobald das Inventar synchronisiert wird. Die Sternenkarte zu besuchen, sollte der einfachste Weg sein, dies auszulösen.`,
 | 
					    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: `Hinzufügen`,
 | 
					    general_addButton: `Hinzufügen`,
 | 
				
			||||||
    general_bulkActions: `Massenaktionen`,
 | 
					    general_bulkActions: `Massenaktionen`,
 | 
				
			||||||
    code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
 | 
					    code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
 | 
				
			||||||
@ -159,6 +159,8 @@ dict = {
 | 
				
			|||||||
    cheats_noDeathMarks: `Keine Todesmarkierungen`,
 | 
					    cheats_noDeathMarks: `Keine Todesmarkierungen`,
 | 
				
			||||||
    cheats_noKimCooldowns: `Keine Wartezeit bei KIM`,
 | 
					    cheats_noKimCooldowns: `Keine Wartezeit bei KIM`,
 | 
				
			||||||
    cheats_fullyStockedVendors: `[UNTRANSLATED] Fully Stocked Vendors`,
 | 
					    cheats_fullyStockedVendors: `[UNTRANSLATED] Fully Stocked Vendors`,
 | 
				
			||||||
 | 
					    cheats_baroAlwaysAvailable: `[UNTRANSLATED] Baro Always Available`,
 | 
				
			||||||
 | 
					    cheats_baroFullyStocked: `[UNTRANSLATED] Baro Fully Stocked`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
 | 
					    cheats_syndicateMissionsRepeatable: `Syndikat-Missionen wiederholbar`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `[UNTRANSLATED] Unlock All Profit Taker Stages`,
 | 
					    cheats_unlockAllProfitTakerStages: `[UNTRANSLATED] Unlock All Profit Taker Stages`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Riven-Mod Herausforderung sofort abschließen`,
 | 
					    cheats_instantFinishRivenChallenge: `Riven-Mod Herausforderung sofort abschließen`,
 | 
				
			||||||
@ -173,18 +175,69 @@ dict = {
 | 
				
			|||||||
    cheats_fastClanAscension: `Schneller Clan-Aufstieg`,
 | 
					    cheats_fastClanAscension: `Schneller Clan-Aufstieg`,
 | 
				
			||||||
    cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
 | 
					    cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
 | 
				
			||||||
    cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
 | 
					    cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
 | 
				
			||||||
 | 
					    cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
 | 
				
			||||||
    cheats_spoofMasteryRank: `Gefälschter Meisterschaftsrang (-1 zum deaktivieren)`,
 | 
					    cheats_spoofMasteryRank: `Gefälschter Meisterschaftsrang (-1 zum deaktivieren)`,
 | 
				
			||||||
    cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
 | 
					    cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
 | 
				
			||||||
    cheats_save: `[UNTRANSLATED] Save`,
 | 
					    cheats_save: `[UNTRANSLATED] Save`,
 | 
				
			||||||
    cheats_account: `Account`,
 | 
					    cheats_account: `Account`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Alle Fokus-Schulen freischalten`,
 | 
					    cheats_unlockAllFocusSchools: `Alle Fokus-Schulen freischalten`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Helminth vollständig aufleveln`,
 | 
					    cheats_helminthUnlockAll: `Helminth vollständig aufleveln`,
 | 
				
			||||||
 | 
					    cheats_addMissingSubsumedAbilities: `[UNTRANSLATED] Add Missing Subsumed Abilities`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Alle Inhärenzen auf Max. Rang`,
 | 
					    cheats_intrinsicsUnlockAll: `Alle Inhärenzen auf Max. Rang`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
 | 
					    cheats_changeSupportedSyndicate: `Unterstütztes Syndikat`,
 | 
				
			||||||
    cheats_changeButton: `Ändern`,
 | 
					    cheats_changeButton: `Ändern`,
 | 
				
			||||||
    cheats_none: `Keines`,
 | 
					    cheats_none: `Keines`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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_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_submit: `Absenden`,
 | 
				
			||||||
 | 
					    import_samples: `[UNTRANSLATED] Samples:`,
 | 
				
			||||||
 | 
					    import_samples_maxFocus: `[UNTRANSLATED] All Focus Schools Maxed Out`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    upgrade_Equilibrium: `[UNTRANSLATED] +|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
 | 
					    upgrade_Equilibrium: `[UNTRANSLATED] +|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
 | 
				
			||||||
    upgrade_MeleeCritDamage: `[UNTRANSLATED] +|VAL|% Melee Critical Damage`,
 | 
					    upgrade_MeleeCritDamage: `[UNTRANSLATED] +|VAL|% Melee Critical Damage`,
 | 
				
			||||||
@ -227,7 +280,7 @@ dict = {
 | 
				
			|||||||
    upgrade_DamageReductionOnHack: `[UNTRANSLATED] 75% Damage Reduction while Hacking`,
 | 
					    upgrade_DamageReductionOnHack: `[UNTRANSLATED] 75% Damage Reduction while Hacking`,
 | 
				
			||||||
    upgrade_OnExecutionReviveCompanion: `[UNTRANSLATED] Mercy Kills reduce Companion Recovery by 15s`,
 | 
					    upgrade_OnExecutionReviveCompanion: `[UNTRANSLATED] Mercy Kills reduce Companion Recovery by 15s`,
 | 
				
			||||||
    upgrade_OnExecutionParkourSpeed: `[UNTRANSLATED] +60% Parkour Speed after a Mercy for 15s`,
 | 
					    upgrade_OnExecutionParkourSpeed: `[UNTRANSLATED] +60% Parkour Speed after a Mercy for 15s`,
 | 
				
			||||||
    upgrade_AvatarTimeLimitIncrease: `[UNTRANSLATED] s to Hacking`,
 | 
					    upgrade_AvatarTimeLimitIncrease: `[UNTRANSLATED] +8s to Hacking`,
 | 
				
			||||||
    upgrade_ElectrifyOnHack: `[UNTRANSLATED] Shock enemies within 20m while 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_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_OnHackLockers: `[UNTRANSLATED] Unlock 5 lockers within 20m after Hacking`,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `Note: Changes made here will only be applied in-game when the game syncs the inventory. Visiting the navigation should be the easiest way to trigger that.`,
 | 
					    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_addButton: `Add`,
 | 
				
			||||||
    general_bulkActions: `Bulk Actions`,
 | 
					    general_bulkActions: `Bulk Actions`,
 | 
				
			||||||
    code_loginFail: `Login failed. Double-check the email and password.`,
 | 
					    code_loginFail: `Login failed. Double-check the email and password.`,
 | 
				
			||||||
@ -158,6 +158,8 @@ dict = {
 | 
				
			|||||||
    cheats_noDeathMarks: `No Death Marks`,
 | 
					    cheats_noDeathMarks: `No Death Marks`,
 | 
				
			||||||
    cheats_noKimCooldowns: `No KIM Cooldowns`,
 | 
					    cheats_noKimCooldowns: `No KIM Cooldowns`,
 | 
				
			||||||
    cheats_fullyStockedVendors: `Fully Stocked Vendors`,
 | 
					    cheats_fullyStockedVendors: `Fully Stocked Vendors`,
 | 
				
			||||||
 | 
					    cheats_baroAlwaysAvailable: `Baro Always Available`,
 | 
				
			||||||
 | 
					    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_instantFinishRivenChallenge: `Instant Finish Riven Challenge`,
 | 
					    cheats_instantFinishRivenChallenge: `Instant Finish Riven Challenge`,
 | 
				
			||||||
@ -172,18 +174,69 @@ dict = {
 | 
				
			|||||||
    cheats_fastClanAscension: `Fast Clan Ascension`,
 | 
					    cheats_fastClanAscension: `Fast Clan Ascension`,
 | 
				
			||||||
    cheats_missionsCanGiveAllRelics: `Missions Can Give All Relics`,
 | 
					    cheats_missionsCanGiveAllRelics: `Missions Can Give All Relics`,
 | 
				
			||||||
    cheats_unlockAllSimarisResearchEntries: `Unlock All Simaris Research Entries`,
 | 
					    cheats_unlockAllSimarisResearchEntries: `Unlock All Simaris Research Entries`,
 | 
				
			||||||
 | 
					    cheats_disableDailyTribute: `Disable Daily Tribute`,
 | 
				
			||||||
    cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`,
 | 
					    cheats_spoofMasteryRank: `Spoofed Mastery Rank (-1 to disable)`,
 | 
				
			||||||
    cheats_nightwaveStandingMultiplier: `Nightwave Standing Multiplier`,
 | 
					    cheats_nightwaveStandingMultiplier: `Nightwave Standing Multiplier`,
 | 
				
			||||||
    cheats_save: `Save`,
 | 
					    cheats_save: `Save`,
 | 
				
			||||||
    cheats_account: `Account`,
 | 
					    cheats_account: `Account`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Unlock All Focus Schools`,
 | 
					    cheats_unlockAllFocusSchools: `Unlock All Focus Schools`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Fully Level Up Helminth`,
 | 
					    cheats_helminthUnlockAll: `Fully Level Up Helminth`,
 | 
				
			||||||
 | 
					    cheats_addMissingSubsumedAbilities: `Add Missing Subsumed Abilities`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Max Rank All Intrinsics`,
 | 
					    cheats_intrinsicsUnlockAll: `Max Rank All Intrinsics`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Supported syndicate`,
 | 
					    cheats_changeSupportedSyndicate: `Supported syndicate`,
 | 
				
			||||||
    cheats_changeButton: `Change`,
 | 
					    cheats_changeButton: `Change`,
 | 
				
			||||||
    cheats_none: `None`,
 | 
					    cheats_none: `None`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    worldState: `World State`,
 | 
				
			||||||
 | 
					    worldState_creditBoost: `Credit Boost`,
 | 
				
			||||||
 | 
					    worldState_affinityBoost: `Affinity Boost`,
 | 
				
			||||||
 | 
					    worldState_resourceBoost: `Resource Boost`,
 | 
				
			||||||
 | 
					    worldState_starDays: `Star Days`,
 | 
				
			||||||
 | 
					    worldState_galleonOfGhouls: `Galleon of Ghouls`,
 | 
				
			||||||
 | 
					    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_vallisOverride: `Orb Vallis Override`,
 | 
				
			||||||
 | 
					    worldState_warm: `Warm`,
 | 
				
			||||||
 | 
					    worldState_cold: `Cold`,
 | 
				
			||||||
 | 
					    worldState_duviriOverride: `Duviri Override`,
 | 
				
			||||||
 | 
					    worldState_joy: `Joy`,
 | 
				
			||||||
 | 
					    worldState_anger: `Anger`,
 | 
				
			||||||
 | 
					    worldState_envy: `Envy`,
 | 
				
			||||||
 | 
					    worldState_sorrow: `Sorrow`,
 | 
				
			||||||
 | 
					    worldState_fear: `Fear`,
 | 
				
			||||||
 | 
					    worldState_nightwaveOverride: `Nightwave Override`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission13Syndicate: `Nora's Mix Vol. 9`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission12Syndicate: `Nora's Mix Vol. 8`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission11Syndicate: `Nora's Mix Vol. 7`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission10Syndicate: `Nora's Mix Vol. 6`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission9Syndicate: `Nora's Mix Vol. 5`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission8Syndicate: `Nora's Mix Vol. 4`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission7Syndicate: `Nora's Mix Vol. 3`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission6Syndicate: `Nora's Mix Vol. 2`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission5Syndicate: `Nora's Mix Vol. 1`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission4Syndicate: `Nora's Choice`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission3Syndicate: `Intermission III`,
 | 
				
			||||||
 | 
					    worldState_RadioLegion3Syndicate: `Glassmaker`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission2Syndicate: `Intermission II`,
 | 
				
			||||||
 | 
					    worldState_RadioLegion2Syndicate: `The Emissary`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermissionSyndicate: `Intermission I`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionSyndicate: `The Wolf of Saturn Six`,
 | 
				
			||||||
 | 
					    worldState_fissures: `Fissures`,
 | 
				
			||||||
 | 
					    normal: `Normal`,
 | 
				
			||||||
 | 
					    worldState_allAtOnceNormal: `All At Once, Normal`,
 | 
				
			||||||
 | 
					    worldState_allAtOnceSteelPath: `All At Once, Steel Path`,
 | 
				
			||||||
 | 
					    worldState_theCircuitOverride: `The Circuit Override`,
 | 
				
			||||||
 | 
					    worldState_darvoStockMultiplier: `Darvo Stock Multiplier`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer <b>will be overwritten</b> in your account.`,
 | 
					    import_importNote: `You can provide a full or partial inventory response (client respresentation) here. All fields that are supported by the importer <b>will be overwritten</b> in your account.`,
 | 
				
			||||||
    import_submit: `Submit`,
 | 
					    import_submit: `Submit`,
 | 
				
			||||||
 | 
					    import_samples: `Samples:`,
 | 
				
			||||||
 | 
					    import_samples_maxFocus: `All Focus Schools Maxed Out`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    upgrade_Equilibrium: `+|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
 | 
					    upgrade_Equilibrium: `+|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
 | 
				
			||||||
    upgrade_MeleeCritDamage: `+|VAL|% Melee Critical Damage`,
 | 
					    upgrade_MeleeCritDamage: `+|VAL|% Melee Critical Damage`,
 | 
				
			||||||
@ -226,7 +279,7 @@ dict = {
 | 
				
			|||||||
    upgrade_DamageReductionOnHack: `75% Damage Reduction while Hacking`,
 | 
					    upgrade_DamageReductionOnHack: `75% Damage Reduction while Hacking`,
 | 
				
			||||||
    upgrade_OnExecutionReviveCompanion: `Mercy Kills reduce Companion Recovery by 15s`,
 | 
					    upgrade_OnExecutionReviveCompanion: `Mercy Kills reduce Companion Recovery by 15s`,
 | 
				
			||||||
    upgrade_OnExecutionParkourSpeed: `+60% Parkour Speed after a Mercy for 15s`,
 | 
					    upgrade_OnExecutionParkourSpeed: `+60% Parkour Speed after a Mercy for 15s`,
 | 
				
			||||||
    upgrade_AvatarTimeLimitIncrease: `s to Hacking`,
 | 
					    upgrade_AvatarTimeLimitIncrease: `+8s to Hacking`,
 | 
				
			||||||
    upgrade_ElectrifyOnHack: `Shock enemies within 20m while Hacking`,
 | 
					    upgrade_ElectrifyOnHack: `Shock enemies within 20m while Hacking`,
 | 
				
			||||||
    upgrade_OnExecutionTerrify: `50% chance for enemies within 15m to cower in fear for 8 seconds on Mercy`,
 | 
					    upgrade_OnExecutionTerrify: `50% chance for enemies within 15m to cower in fear for 8 seconds on Mercy`,
 | 
				
			||||||
    upgrade_OnHackLockers: `Unlock 5 lockers within 20m after Hacking`,
 | 
					    upgrade_OnHackLockers: `Unlock 5 lockers within 20m after Hacking`,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
// Spanish translation by hxedcl
 | 
					// Spanish translation by hxedcl
 | 
				
			||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `Nota: Los cambios realizados aquí se reflejarán en el juego cuando este sincronice el inventario. Usar la navegación debería ser la forma más sencilla de activar esto.`,
 | 
					    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: `Agregar`,
 | 
					    general_addButton: `Agregar`,
 | 
				
			||||||
    general_bulkActions: `Acciones masivas`,
 | 
					    general_bulkActions: `Acciones masivas`,
 | 
				
			||||||
    code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`,
 | 
					    code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`,
 | 
				
			||||||
@ -158,7 +158,9 @@ dict = {
 | 
				
			|||||||
    cheats_noVendorPurchaseLimits: `Sin límite de compras de vendedores`,
 | 
					    cheats_noVendorPurchaseLimits: `Sin límite de compras de vendedores`,
 | 
				
			||||||
    cheats_noDeathMarks: `Sin marcas de muerte`,
 | 
					    cheats_noDeathMarks: `Sin marcas de muerte`,
 | 
				
			||||||
    cheats_noKimCooldowns: `Sin tiempo de espera para conversaciones KIM`,
 | 
					    cheats_noKimCooldowns: `Sin tiempo de espera para conversaciones KIM`,
 | 
				
			||||||
    cheats_fullyStockedVendors: `[UNTRANSLATED] Fully Stocked Vendors`,
 | 
					    cheats_fullyStockedVendors: `Vendedores con stock completo`,
 | 
				
			||||||
 | 
					    cheats_baroAlwaysAvailable: `Baro siempre disponible`,
 | 
				
			||||||
 | 
					    cheats_baroFullyStocked: `Baro con stock completo`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Misiones de sindicato rejugables`,
 | 
					    cheats_syndicateMissionsRepeatable: `Misiones de sindicato rejugables`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `Deslobquea todas las etapas del Roba-ganancias`,
 | 
					    cheats_unlockAllProfitTakerStages: `Deslobquea todas las etapas del Roba-ganancias`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `Terminar desafío de agrietado inmediatamente`,
 | 
					    cheats_instantFinishRivenChallenge: `Terminar desafío de agrietado inmediatamente`,
 | 
				
			||||||
@ -172,19 +174,70 @@ dict = {
 | 
				
			|||||||
    cheats_noDojoResearchTime: `Sin tiempo de investigación del dojo`,
 | 
					    cheats_noDojoResearchTime: `Sin tiempo de investigación del dojo`,
 | 
				
			||||||
    cheats_fastClanAscension: `Ascenso rápido del clan`,
 | 
					    cheats_fastClanAscension: `Ascenso rápido del clan`,
 | 
				
			||||||
    cheats_missionsCanGiveAllRelics: `Las misiones pueden otorgar todas las reliquias`,
 | 
					    cheats_missionsCanGiveAllRelics: `Las misiones pueden otorgar todas las reliquias`,
 | 
				
			||||||
    cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
 | 
					    cheats_unlockAllSimarisResearchEntries: `Desbloquear todas las entradas de investigación de Simaris`,
 | 
				
			||||||
 | 
					    cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
 | 
				
			||||||
    cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`,
 | 
					    cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`,
 | 
				
			||||||
    cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`,
 | 
					    cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`,
 | 
				
			||||||
    cheats_save: `Guardar`,
 | 
					    cheats_save: `Guardar`,
 | 
				
			||||||
    cheats_account: `Cuenta`,
 | 
					    cheats_account: `Cuenta`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Desbloquear todas las escuelas de enfoque`,
 | 
					    cheats_unlockAllFocusSchools: `Desbloquear todas las escuelas de enfoque`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Subir al máximo el Helminto`,
 | 
					    cheats_helminthUnlockAll: `Subir al máximo el Helminto`,
 | 
				
			||||||
 | 
					    cheats_addMissingSubsumedAbilities: `[UNTRANSLATED] Add Missing Subsumed Abilities`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Maximizar todos los intrínsecos`,
 | 
					    cheats_intrinsicsUnlockAll: `Maximizar todos los intrínsecos`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
 | 
					    cheats_changeSupportedSyndicate: `Sindicatos disponibles`,
 | 
				
			||||||
    cheats_changeButton: `Cambiar`,
 | 
					    cheats_changeButton: `Cambiar`,
 | 
				
			||||||
    cheats_none: `Ninguno`,
 | 
					    cheats_none: `Ninguno`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import_importNote: `Puedes proporcionar una respuesta de inventario completa o parcial (representación del cliente) aquí. Todos los campos compatibles con el importador <b>serán sobrescritos</b> en tu cuenta.`,
 | 
					    import_importNote: `Puedes proporcionar una respuesta de inventario completa o parcial (representación del cliente) aquí. Todos los campos compatibles con el importador <b>serán sobrescritos</b> en tu cuenta.`,
 | 
				
			||||||
    import_submit: `Enviar`,
 | 
					    import_submit: `Enviar`,
 | 
				
			||||||
 | 
					    import_samples: `Muestras:`,
 | 
				
			||||||
 | 
					    import_samples_maxFocus: `Todas las escuelas de enfoque al máximo`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    upgrade_Equilibrium: `+|VAL|% de Energía al recoger salud, +|VAL|% de Salud al recoger energía`,
 | 
					    upgrade_Equilibrium: `+|VAL|% de Energía al recoger salud, +|VAL|% de Salud al recoger energía`,
 | 
				
			||||||
    upgrade_MeleeCritDamage: `+|VAL|% de daño crítico cuerpo a cuerpo`,
 | 
					    upgrade_MeleeCritDamage: `+|VAL|% de daño crítico cuerpo a cuerpo`,
 | 
				
			||||||
@ -227,7 +280,7 @@ dict = {
 | 
				
			|||||||
    upgrade_DamageReductionOnHack: `75% de reducción de daño al hackear`,
 | 
					    upgrade_DamageReductionOnHack: `75% de reducción de daño al hackear`,
 | 
				
			||||||
    upgrade_OnExecutionReviveCompanion: `Las ejecuciones reducen el tiempo de recuperación del compañero en 15s`,
 | 
					    upgrade_OnExecutionReviveCompanion: `Las ejecuciones reducen el tiempo de recuperación del compañero en 15s`,
 | 
				
			||||||
    upgrade_OnExecutionParkourSpeed: `+60% de velocidad de parkour durante 15s tras una ejecución`,
 | 
					    upgrade_OnExecutionParkourSpeed: `+60% de velocidad de parkour durante 15s tras una ejecución`,
 | 
				
			||||||
    upgrade_AvatarTimeLimitIncrease: `+|VAL|s al tiempo de hackeo`,
 | 
					    upgrade_AvatarTimeLimitIncrease: `[UNTRANSLATED] +8s to Hacking`,
 | 
				
			||||||
    upgrade_ElectrifyOnHack: `Electrocuta a los enemigos en un radio de 20m al hackear`,
 | 
					    upgrade_ElectrifyOnHack: `Electrocuta a los enemigos en un radio de 20m al hackear`,
 | 
				
			||||||
    upgrade_OnExecutionTerrify: `50% de probabilidad de que enemigos en un radio de 15m entren en pánico por 8s tras una ejecución`,
 | 
					    upgrade_OnExecutionTerrify: `50% de probabilidad de que enemigos en un radio de 15m entren en pánico por 8s tras una ejecución`,
 | 
				
			||||||
    upgrade_OnHackLockers: `Desbloquea 5 casilleros en un radio de 20m tras hackear`,
 | 
					    upgrade_OnHackLockers: `Desbloquea 5 casilleros en un radio de 20m tras hackear`,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
// French translation by Vitruvio
 | 
					// French translation by Vitruvio
 | 
				
			||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `Note : Les changements effectués ici seront appliqués lors de la syncrhonisation. Visiter la navigation appliquera les changements apportés à l'inventaire.`,
 | 
					    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: `Ajouter`,
 | 
					    general_addButton: `Ajouter`,
 | 
				
			||||||
    general_bulkActions: `Action groupée`,
 | 
					    general_bulkActions: `Action groupée`,
 | 
				
			||||||
    code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
 | 
					    code_loginFail: `Connexion échouée. Vérifiez le mot de passe.`,
 | 
				
			||||||
    code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
 | 
					    code_regFail: `Enregistrement impossible. Compte existant?`,
 | 
				
			||||||
    code_changeNameConfirm: `Nouveau nom du compte :`,
 | 
					    code_changeNameConfirm: `Nouveau nom du compte :`,
 | 
				
			||||||
    code_deleteAccountConfirm: `Supprimer |DISPLAYNAME| (|EMAIL|) ? Cette action est irreversible.`,
 | 
					    code_deleteAccountConfirm: `Supprimer |DISPLAYNAME| (|EMAIL|) ? Cette action est irreversible.`,
 | 
				
			||||||
    code_archgun: `Archgun`,
 | 
					    code_archgun: `Archgun`,
 | 
				
			||||||
@ -85,7 +85,7 @@ dict = {
 | 
				
			|||||||
    inventory_moaPets: `Moas`,
 | 
					    inventory_moaPets: `Moas`,
 | 
				
			||||||
    inventory_kubrowPets: `Bêtes`,
 | 
					    inventory_kubrowPets: `Bêtes`,
 | 
				
			||||||
    inventory_evolutionProgress: `Progrès de l'évolution Incarnon`,
 | 
					    inventory_evolutionProgress: `Progrès de l'évolution Incarnon`,
 | 
				
			||||||
    inventory_Boosters: `[UNTRANSLATED] Boosters`,
 | 
					    inventory_Boosters: `Boosters`,
 | 
				
			||||||
    inventory_bulkAddSuits: `Ajouter les Warframes manquantes`,
 | 
					    inventory_bulkAddSuits: `Ajouter les Warframes manquantes`,
 | 
				
			||||||
    inventory_bulkAddWeapons: `Ajouter les armes manquantes`,
 | 
					    inventory_bulkAddWeapons: `Ajouter les armes manquantes`,
 | 
				
			||||||
    inventory_bulkAddSpaceSuits: `Ajouter les Archwings manquants`,
 | 
					    inventory_bulkAddSpaceSuits: `Ajouter les Archwings manquants`,
 | 
				
			||||||
@ -100,7 +100,7 @@ dict = {
 | 
				
			|||||||
    inventory_bulkRankUpSentinels: `Toutes les Sentinelles au rang max`,
 | 
					    inventory_bulkRankUpSentinels: `Toutes les Sentinelles au rang max`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `Toutes les armes de Sentinelles au rang max`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `Toutes les armes de Sentinelles au rang max`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `Toutes les évolutions Incarnon au rang max`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `Toutes les évolutions Incarnon au rang max`,
 | 
				
			||||||
    inventory_maxPlexus: `[UNTRANSLATED] Max Rank Plexus`,
 | 
					    inventory_maxPlexus: `Plexus au rang max`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quests_list: `Quêtes`,
 | 
					    quests_list: `Quêtes`,
 | 
				
			||||||
    quests_completeAll: `Compléter toutes les quêtes`,
 | 
					    quests_completeAll: `Compléter toutes les quêtes`,
 | 
				
			||||||
@ -135,10 +135,10 @@ dict = {
 | 
				
			|||||||
    cheats_infiniteRegalAya: `Aya Raffiné infini`,
 | 
					    cheats_infiniteRegalAya: `Aya Raffiné infini`,
 | 
				
			||||||
    cheats_infiniteHelminthMaterials: `Ressources d'Helminth infinies`,
 | 
					    cheats_infiniteHelminthMaterials: `Ressources d'Helminth infinies`,
 | 
				
			||||||
    cheats_claimingBlueprintRefundsIngredients: `Récupérer les items rend les ressources`,
 | 
					    cheats_claimingBlueprintRefundsIngredients: `Récupérer les items rend les ressources`,
 | 
				
			||||||
    cheats_dontSubtractPurchaseCreditCost: `[UNTRANSLATED] Don't Subtract Purchase Credit Cost`,
 | 
					    cheats_dontSubtractPurchaseCreditCost: `Ne pas retirer le coût en crédits`,
 | 
				
			||||||
    cheats_dontSubtractPurchasePlatinumCost: `[UNTRANSLATED] Don't Subtract Purchase Platinum Cost`,
 | 
					    cheats_dontSubtractPurchasePlatinumCost: `Ne pas retirer le coût en platines`,
 | 
				
			||||||
    cheats_dontSubtractPurchaseItemCost: `[UNTRANSLATED] Don't Subtract Purchase Item Cost`,
 | 
					    cheats_dontSubtractPurchaseItemCost: `Ne pas retirer le coût d'achat`,
 | 
				
			||||||
    cheats_dontSubtractPurchaseStandingCost: `[UNTRANSLATED] Don't Subtract Purchase Standing Cost`,
 | 
					    cheats_dontSubtractPurchaseStandingCost: `Ne pas retirer le coût en réputation`,
 | 
				
			||||||
    cheats_dontSubtractVoidTraces: `Ne pas consommer de Void Traces`,
 | 
					    cheats_dontSubtractVoidTraces: `Ne pas consommer de Void Traces`,
 | 
				
			||||||
    cheats_dontSubtractConsumables: `Ne pas retirer de consommables`,
 | 
					    cheats_dontSubtractConsumables: `Ne pas retirer de consommables`,
 | 
				
			||||||
    cheats_unlockAllShipFeatures: `Débloquer tous les segments du vaisseau`,
 | 
					    cheats_unlockAllShipFeatures: `Débloquer tous les segments du vaisseau`,
 | 
				
			||||||
@ -158,9 +158,11 @@ dict = {
 | 
				
			|||||||
    cheats_noVendorPurchaseLimits: `Aucune limite d'achat chez les PNJ`,
 | 
					    cheats_noVendorPurchaseLimits: `Aucune limite d'achat chez les PNJ`,
 | 
				
			||||||
    cheats_noDeathMarks: `Aucune marque d'assassin`,
 | 
					    cheats_noDeathMarks: `Aucune marque d'assassin`,
 | 
				
			||||||
    cheats_noKimCooldowns: `Aucun cooldown sur le KIM`,
 | 
					    cheats_noKimCooldowns: `Aucun cooldown sur le KIM`,
 | 
				
			||||||
    cheats_fullyStockedVendors: `[UNTRANSLATED] Fully Stocked Vendors`,
 | 
					    cheats_fullyStockedVendors: `Les vendeurs ont un stock à 100%`,
 | 
				
			||||||
 | 
					    cheats_baroAlwaysAvailable: `[UNTRANSLATED] Baro Always Available`,
 | 
				
			||||||
 | 
					    cheats_baroFullyStocked: `[UNTRANSLATED] Baro Fully Stocked`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `Mission syndicat répétables`,
 | 
					    cheats_syndicateMissionsRepeatable: `Mission syndicat répétables`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `[UNTRANSLATED] Unlock All Profit Taker Stages`,
 | 
					    cheats_unlockAllProfitTakerStages: `Débloquer toutes les étapes du Preneur de Profit`,
 | 
				
			||||||
    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,71 +173,122 @@ dict = {
 | 
				
			|||||||
    cheats_noDojoResearchCosts: `Aucun coût de recherche (Dojo)`,
 | 
					    cheats_noDojoResearchCosts: `Aucun coût de recherche (Dojo)`,
 | 
				
			||||||
    cheats_noDojoResearchTime: `Aucun temps de recherche (Dojo)`,
 | 
					    cheats_noDojoResearchTime: `Aucun temps de recherche (Dojo)`,
 | 
				
			||||||
    cheats_fastClanAscension: `Ascension de clan rapide`,
 | 
					    cheats_fastClanAscension: `Ascension de clan rapide`,
 | 
				
			||||||
    cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
 | 
					    cheats_missionsCanGiveAllRelics: `Les missions donnent toutes les reliques`,
 | 
				
			||||||
    cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
 | 
					    cheats_unlockAllSimarisResearchEntries: `Débloquer toute les recherches chez Simaris`,
 | 
				
			||||||
 | 
					    cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
 | 
				
			||||||
    cheats_spoofMasteryRank: `Rang de maîtrise personnalisé (-1 pour désactiver)`,
 | 
					    cheats_spoofMasteryRank: `Rang de maîtrise personnalisé (-1 pour désactiver)`,
 | 
				
			||||||
    cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
 | 
					    cheats_nightwaveStandingMultiplier: `Multiplicateur de réputation d'Ondes Nocturnes`,
 | 
				
			||||||
    cheats_save: `[UNTRANSLATED] Save`,
 | 
					    cheats_save: `Sauvegarder`,
 | 
				
			||||||
    cheats_account: `Compte`,
 | 
					    cheats_account: `Compte`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Débloquer toutes les écoles de focus`,
 | 
					    cheats_unlockAllFocusSchools: `Débloquer toutes les écoles de focus`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Helminth niveau max`,
 | 
					    cheats_helminthUnlockAll: `Helminth niveau max`,
 | 
				
			||||||
 | 
					    cheats_addMissingSubsumedAbilities: `[UNTRANSLATED] Add Missing Subsumed Abilities`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Inhérences niveau max`,
 | 
					    cheats_intrinsicsUnlockAll: `Inhérences niveau max`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Allégeance`,
 | 
					    cheats_changeSupportedSyndicate: `Allégeance`,
 | 
				
			||||||
    cheats_changeButton: `Changer`,
 | 
					    cheats_changeButton: `Changer`,
 | 
				
			||||||
    cheats_none: `Aucun`,
 | 
					    cheats_none: `Aucun`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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_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_submit: `Soumettre`,
 | 
				
			||||||
 | 
					    import_samples: `Echantillons :`,
 | 
				
			||||||
 | 
					    import_samples_maxFocus: `Toutes les écoles de focus au rang max`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    upgrade_Equilibrium: `[UNTRANSLATED] +|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
 | 
					    upgrade_Equilibrium: `Ramasser de la santé donne +|VAL|% d'énergie supplémentaire. Ramasser de l'énergie donne +|VAL|% de santé supplémentaire.`,
 | 
				
			||||||
    upgrade_MeleeCritDamage: `[UNTRANSLATED] +|VAL|% Melee Critical Damage`,
 | 
					    upgrade_MeleeCritDamage: `+|VAL|% de dégâts critique en mêlée`,
 | 
				
			||||||
    upgrade_PrimaryStatusChance: `[UNTRANSLATED] +|VAL|% Primary Status Chance`,
 | 
					    upgrade_PrimaryStatusChance: `+|VAL|% de chance de statut sur arme primaire`,
 | 
				
			||||||
    upgrade_SecondaryCritChance: `[UNTRANSLATED] +|VAL|% Secondary Critical Chance`,
 | 
					    upgrade_SecondaryCritChance: `+|VAL|% de chance critique sur arme secondaire`,
 | 
				
			||||||
    upgrade_WarframeAbilityDuration: `[UNTRANSLATED] +|VAL|% Ability Duration`,
 | 
					    upgrade_WarframeAbilityDuration: `+|VAL|% de durée de pouvoir`,
 | 
				
			||||||
    upgrade_WarframeAbilityStrength: `[UNTRANSLATED] +|VAL|% Ability Strength`,
 | 
					    upgrade_WarframeAbilityStrength: `+|VAL|% de puissance de pouvoir`,
 | 
				
			||||||
    upgrade_WarframeArmourMax: `[UNTRANSLATED] +|VAL| Armor`,
 | 
					    upgrade_WarframeArmourMax: `+|VAL| d'armure`,
 | 
				
			||||||
    upgrade_WarframeBlastProc: `[UNTRANSLATED] +|VAL| Shields on kill with Blast Damage`,
 | 
					    upgrade_WarframeBlastProc: `+|VAL| de boucliers sur élimination avec des dégats d'explosion`,
 | 
				
			||||||
    upgrade_WarframeCastingSpeed: `[UNTRANSLATED] +|VAL|% Casting Speed`,
 | 
					    upgrade_WarframeCastingSpeed: `+|VAL|% de vitesse de lancement de pouvoir`,
 | 
				
			||||||
    upgrade_WarframeCorrosiveDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Corrosion Status`,
 | 
					    upgrade_WarframeCorrosiveDamageBoost: `+|VAL|% de dégâts de pouvoir sur les ennemis affectés par du statut corrosif`,
 | 
				
			||||||
    upgrade_WarframeCorrosiveStack: `[UNTRANSLATED] Increase max stacks of Corrosion Status by +|VAL|`,
 | 
					    upgrade_WarframeCorrosiveStack: `+|VAL| de cumuls maximum de Statut Corrosif`,
 | 
				
			||||||
    upgrade_WarframeCritDamageBoost: `[UNTRANSLATED] +|VAL|% Melee Critical Damage (Doubles over 500 Energy)`,
 | 
					    upgrade_WarframeCritDamageBoost: `+|VAL|% de dégâts critique en mêlée (Doublé si l'énergie dépasse 500)`,
 | 
				
			||||||
    upgrade_WarframeElectricDamage: `[UNTRANSLATED] +|VAL1|% Primary Electricity Damage (+|VAL2|% per additional Shard)`,
 | 
					    upgrade_WarframeElectricDamage: `+|VAL1|% de dégâts électrique sur arme primaire (+|VAL2|% par fragment supplémentaire)`,
 | 
				
			||||||
    upgrade_WarframeElectricDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Electricity Status`,
 | 
					    upgrade_WarframeElectricDamageBoost: `+|VAL|% de dégâts de pouvoir sur les ennemis affectés par du statut électrique`,
 | 
				
			||||||
    upgrade_WarframeEnergyMax: `[UNTRANSLATED] +|VAL| Energy Max`,
 | 
					    upgrade_WarframeEnergyMax: `+|VAL| d'énergie max`,
 | 
				
			||||||
    upgrade_WarframeGlobeEffectEnergy: `[UNTRANSLATED] +|VAL|% Energy Orb Effectiveness`,
 | 
					    upgrade_WarframeGlobeEffectEnergy: `+|VAL|% d'efficacité d'orbe d'énergie`,
 | 
				
			||||||
    upgrade_WarframeGlobeEffectHealth: `[UNTRANSLATED] +|VAL|% Health Orb Effectiveness`,
 | 
					    upgrade_WarframeGlobeEffectHealth: `+|VAL|% d'efficacité d'orbe de santé`,
 | 
				
			||||||
    upgrade_WarframeHealthMax: `[UNTRANSLATED] +|VAL| Health`,
 | 
					    upgrade_WarframeHealthMax: `+|VAL| de santé`,
 | 
				
			||||||
    upgrade_WarframeHPBoostFromImpact: `[UNTRANSLATED] +|VAL1| Health per enemy killed with Blast Damage (Max |VAL2| Health)`,
 | 
					    upgrade_WarframeHPBoostFromImpact: `+|VAL1| de santé par ennemi tué avec des dégâts explosifs (Max |VAL2| de santé)`,
 | 
				
			||||||
    upgrade_WarframeParkourVelocity: `[UNTRANSLATED] +|VAL|% Parkour Velocity`,
 | 
					    upgrade_WarframeParkourVelocity: `+|VAL|% de vélocité de parkour`,
 | 
				
			||||||
    upgrade_WarframeRadiationDamageBoost: `[UNTRANSLATED] +|VAL|% Ability Damage on enemies affected by Radiation Status`,
 | 
					    upgrade_WarframeRadiationDamageBoost: `+|VAL|% de dégâts de pouvoir sur les ennemis affectés par du statut radiation`,
 | 
				
			||||||
    upgrade_WarframeRegen: `[UNTRANSLATED] +|VAL| Health Regen/s`,
 | 
					    upgrade_WarframeRegen: `+|VAL| régénération de santé/s`,
 | 
				
			||||||
    upgrade_WarframeShieldMax: `[UNTRANSLATED] +|VAL| Shield`,
 | 
					    upgrade_WarframeShieldMax: `+|VAL| de boucliers`,
 | 
				
			||||||
    upgrade_WarframeStartingEnergy: `[UNTRANSLATED] +|VAL|% Energy on Spawn`,
 | 
					    upgrade_WarframeStartingEnergy: `+|VAL|% d'énergie sur apparition`,
 | 
				
			||||||
    upgrade_WarframeToxinDamage: `[UNTRANSLATED] +|VAL|% Toxin Status Effect Damage`,
 | 
					    upgrade_WarframeToxinDamage: `+|VAL|% de dégâts sur le statut poison`,
 | 
				
			||||||
    upgrade_WarframeToxinHeal: `[UNTRANSLATED] +|VAL| Health on damaging enemies with Toxin Status`,
 | 
					    upgrade_WarframeToxinHeal: `+|VAL| de santé récupérée à chaque dégât de statut poison`,
 | 
				
			||||||
    upgrade_WeaponCritBoostFromHeat: `[UNTRANSLATED] +|VAL1|% Secondary Critical Chance per Heat-affected enemy killed (Max |VAL2|%)`,
 | 
					    upgrade_WeaponCritBoostFromHeat: `+|VAL1|% de chance critique sur arme secondaire pour chaque ennemi affecté puis tué par du feu (Max |VAL2|%)`,
 | 
				
			||||||
    upgrade_AvatarAbilityRange: `[UNTRANSLATED] +7.5% Ability Range`,
 | 
					    upgrade_AvatarAbilityRange: `+7.5% de portée de pouvoir`,
 | 
				
			||||||
    upgrade_AvatarAbilityEfficiency: `[UNTRANSLATED] +5% Ability Efficiency`,
 | 
					    upgrade_AvatarAbilityEfficiency: `+5% d'efficacité de pouvoir`,
 | 
				
			||||||
    upgrade_AvatarEnergyRegen: `[UNTRANSLATED] +0.5 Energy Regen/s`,
 | 
					    upgrade_AvatarEnergyRegen: `+0.5 de régénération d'énergie/s`,
 | 
				
			||||||
    upgrade_AvatarEnemyRadar: `[UNTRANSLATED] +5m Enemy Radar`,
 | 
					    upgrade_AvatarEnemyRadar: `+5m de radar ennemi`,
 | 
				
			||||||
    upgrade_AvatarLootRadar: `[UNTRANSLATED] +7m Loot Radar`,
 | 
					    upgrade_AvatarLootRadar: `+7m de radar à butin`,
 | 
				
			||||||
    upgrade_WeaponAmmoMax: `[UNTRANSLATED] +15% Ammo Max`,
 | 
					    upgrade_WeaponAmmoMax: `+15% de munitions max`,
 | 
				
			||||||
    upgrade_EnemyArmorReductionAura: `[UNTRANSLATED] -3% Enemy Armor`,
 | 
					    upgrade_EnemyArmorReductionAura: `-3% d'armure ennemi`,
 | 
				
			||||||
    upgrade_OnExecutionAmmo: `[UNTRANSLATED] 100% Primary and Secondary Magazine Refill on Mercy`,
 | 
					    upgrade_OnExecutionAmmo: `100% de rechargement des armes primaires et secondaires sur une une miséricorde`,
 | 
				
			||||||
    upgrade_OnExecutionHealthDrop: `[UNTRANSLATED] 100% chance to drop a Health Orb on Mercy`,
 | 
					    upgrade_OnExecutionHealthDrop: `100% de chance de drop une orbe de santé sur une miséricorde`,
 | 
				
			||||||
    upgrade_OnExecutionEnergyDrop: `[UNTRANSLATED] 50% chance to drop an Energy Orb on Mercy`,
 | 
					    upgrade_OnExecutionEnergyDrop: `50% de chance de drop une orbe d'énergie sur une miséricorde`,
 | 
				
			||||||
    upgrade_OnFailHackReset: `[UNTRANSLATED] +50% to retry on Hacking failure`,
 | 
					    upgrade_OnFailHackReset: `[UNTRANSLATED] +50% to retry on Hacking failure`,
 | 
				
			||||||
    upgrade_DamageReductionOnHack: `[UNTRANSLATED] 75% Damage Reduction while Hacking`,
 | 
					    upgrade_DamageReductionOnHack: `75% de réduction de dégâts pendant un piratage`,
 | 
				
			||||||
    upgrade_OnExecutionReviveCompanion: `[UNTRANSLATED] Mercy Kills reduce Companion Recovery by 15s`,
 | 
					    upgrade_OnExecutionReviveCompanion: `Les miséricordes réduisent le temps de récupération du compagnon de 15s`,
 | 
				
			||||||
    upgrade_OnExecutionParkourSpeed: `[UNTRANSLATED] +60% Parkour Speed after a Mercy for 15s`,
 | 
					    upgrade_OnExecutionParkourSpeed: `+60% de vitesse de parkour pendant 15s après une miséricorde`,
 | 
				
			||||||
    upgrade_AvatarTimeLimitIncrease: `[UNTRANSLATED] s to Hacking`,
 | 
					    upgrade_AvatarTimeLimitIncrease: `+8s de temps de piratage`,
 | 
				
			||||||
    upgrade_ElectrifyOnHack: `[UNTRANSLATED] Shock enemies within 20m while Hacking`,
 | 
					    upgrade_ElectrifyOnHack: `Electrifie les ennemis dans un rayon de 20m pendant un piratage`,
 | 
				
			||||||
    upgrade_OnExecutionTerrify: `[UNTRANSLATED] 50% chance for enemies within 15m to cower in fear for 8 seconds on Mercy`,
 | 
					    upgrade_OnExecutionTerrify: `Les ennemis dans un rayon de 15m ont 50% de chance de s'enfuir après une miséricorde`,
 | 
				
			||||||
    upgrade_OnHackLockers: `[UNTRANSLATED] Unlock 5 lockers within 20m after Hacking`,
 | 
					    upgrade_OnHackLockers: `5 casiers s'ouvrent dans un rayon de 20m après un piratage`,
 | 
				
			||||||
    upgrade_OnExecutionBlind: `[UNTRANSLATED] Blind enemies within 18m on Mercy`,
 | 
					    upgrade_OnExecutionBlind: `Les ennemis sont aveuglés dans un rayon de 18 après une miséricorde`,
 | 
				
			||||||
    upgrade_OnExecutionDrainPower: `[UNTRANSLATED] 100% chance for next ability cast to gain +50% Ability Strength on Mercy`,
 | 
					    upgrade_OnExecutionDrainPower: `100% pour le prochain pouvoir de gagner +50% de puissance de pouvoir sur miséricorde`,
 | 
				
			||||||
    upgrade_OnHackSprintSpeed: `[UNTRANSLATED] +75% Sprint Speed for 15s after Hacking`,
 | 
					    upgrade_OnHackSprintSpeed: `+75% de vitesse de course pendant 15s après un piratage`,
 | 
				
			||||||
    upgrade_SwiftExecute: `[UNTRANSLATED] Speed of Mercy Kills increased by 50%`,
 | 
					    upgrade_SwiftExecute: `Vitesse des miséricordes augmentée de 50%`,
 | 
				
			||||||
    upgrade_OnHackInvis: `[UNTRANSLATED] Invisible for 15 seconds after hacking`,
 | 
					    upgrade_OnHackInvis: `Invisible pendant 15 secondes après un piratage`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    prettier_sucks_ass: ``
 | 
					    prettier_sucks_ass: ``
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
// Russian translation by AMelonInsideLemon
 | 
					// Russian translation by AMelonInsideLemon
 | 
				
			||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `Примечание: изменения, внесенные здесь, отобразятся в игре только после повторной загрузки вашего инвентаря. Посещение навигации — самый простой способ этого добиться.`,
 | 
					    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_addButton: `Добавить`,
 | 
				
			||||||
    general_bulkActions: `Массовые действия`,
 | 
					    general_bulkActions: `Массовые действия`,
 | 
				
			||||||
    code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
 | 
					    code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
 | 
				
			||||||
@ -159,6 +159,8 @@ dict = {
 | 
				
			|||||||
    cheats_noDeathMarks: `Без меток сметри`,
 | 
					    cheats_noDeathMarks: `Без меток сметри`,
 | 
				
			||||||
    cheats_noKimCooldowns: `Чаты KIM без кулдауна`,
 | 
					    cheats_noKimCooldowns: `Чаты KIM без кулдауна`,
 | 
				
			||||||
    cheats_fullyStockedVendors: `[UNTRANSLATED] Fully Stocked Vendors`,
 | 
					    cheats_fullyStockedVendors: `[UNTRANSLATED] Fully Stocked Vendors`,
 | 
				
			||||||
 | 
					    cheats_baroAlwaysAvailable: `[UNTRANSLATED] Baro Always Available`,
 | 
				
			||||||
 | 
					    cheats_baroFullyStocked: `[UNTRANSLATED] Baro Fully Stocked`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `[UNTRANSLATED] Syndicate Missions Repeatable`,
 | 
					    cheats_syndicateMissionsRepeatable: `[UNTRANSLATED] Syndicate Missions Repeatable`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `[UNTRANSLATED] Unlock All Profit Taker Stages`,
 | 
					    cheats_unlockAllProfitTakerStages: `[UNTRANSLATED] Unlock All Profit Taker Stages`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `[UNTRANSLATED] Instant Finish Riven Challenge`,
 | 
					    cheats_instantFinishRivenChallenge: `[UNTRANSLATED] Instant Finish Riven Challenge`,
 | 
				
			||||||
@ -173,18 +175,69 @@ dict = {
 | 
				
			|||||||
    cheats_fastClanAscension: `Мгновенное Вознесение Клана`,
 | 
					    cheats_fastClanAscension: `Мгновенное Вознесение Клана`,
 | 
				
			||||||
    cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
 | 
					    cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
 | 
				
			||||||
    cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
 | 
					    cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
 | 
				
			||||||
 | 
					    cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
 | 
				
			||||||
    cheats_spoofMasteryRank: `Подделанный ранг мастерства (-1 для отключения)`,
 | 
					    cheats_spoofMasteryRank: `Подделанный ранг мастерства (-1 для отключения)`,
 | 
				
			||||||
    cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
 | 
					    cheats_nightwaveStandingMultiplier: `[UNTRANSLATED] Nightwave Standing Multiplier`,
 | 
				
			||||||
    cheats_save: `[UNTRANSLATED] Save`,
 | 
					    cheats_save: `[UNTRANSLATED] Save`,
 | 
				
			||||||
    cheats_account: `Аккаунт`,
 | 
					    cheats_account: `Аккаунт`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `Разблокировать все школы фокуса`,
 | 
					    cheats_unlockAllFocusSchools: `Разблокировать все школы фокуса`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `Полностью улучшить Гельминта`,
 | 
					    cheats_helminthUnlockAll: `Полностью улучшить Гельминта`,
 | 
				
			||||||
 | 
					    cheats_addMissingSubsumedAbilities: `Добавить отсутствующие поглощённые способности`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `Полностью улучшить Модуляры`,
 | 
					    cheats_intrinsicsUnlockAll: `Полностью улучшить Модуляры`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
 | 
					    cheats_changeSupportedSyndicate: `Поддерживаемый синдикат`,
 | 
				
			||||||
    cheats_changeButton: `Изменить`,
 | 
					    cheats_changeButton: `Изменить`,
 | 
				
			||||||
    cheats_none: `Отсутствует`,
 | 
					    cheats_none: `Отсутствует`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля <b>будут перезаписаны</b> в вашем аккаунте.`,
 | 
					    import_importNote: `Вы можете загрузить полный или частичный ответ инвентаря (клиентское представление) здесь. Все поддерживаемые поля <b>будут перезаписаны</b> в вашем аккаунте.`,
 | 
				
			||||||
    import_submit: `Отправить`,
 | 
					    import_submit: `Отправить`,
 | 
				
			||||||
 | 
					    import_samples: `[UNTRANSLATED] Samples:`,
 | 
				
			||||||
 | 
					    import_samples_maxFocus: `[UNTRANSLATED] All Focus Schools Maxed Out`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    upgrade_Equilibrium: `[UNTRANSLATED] +|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
 | 
					    upgrade_Equilibrium: `[UNTRANSLATED] +|VAL|% Energy from Health pickups, +|VAL|% Health from Energy pickups`,
 | 
				
			||||||
    upgrade_MeleeCritDamage: `[UNTRANSLATED] +|VAL|% Melee Critical Damage`,
 | 
					    upgrade_MeleeCritDamage: `[UNTRANSLATED] +|VAL|% Melee Critical Damage`,
 | 
				
			||||||
@ -227,7 +280,7 @@ dict = {
 | 
				
			|||||||
    upgrade_DamageReductionOnHack: `[UNTRANSLATED] 75% Damage Reduction while Hacking`,
 | 
					    upgrade_DamageReductionOnHack: `[UNTRANSLATED] 75% Damage Reduction while Hacking`,
 | 
				
			||||||
    upgrade_OnExecutionReviveCompanion: `[UNTRANSLATED] Mercy Kills reduce Companion Recovery by 15s`,
 | 
					    upgrade_OnExecutionReviveCompanion: `[UNTRANSLATED] Mercy Kills reduce Companion Recovery by 15s`,
 | 
				
			||||||
    upgrade_OnExecutionParkourSpeed: `[UNTRANSLATED] +60% Parkour Speed after a Mercy for 15s`,
 | 
					    upgrade_OnExecutionParkourSpeed: `[UNTRANSLATED] +60% Parkour Speed after a Mercy for 15s`,
 | 
				
			||||||
    upgrade_AvatarTimeLimitIncrease: `[UNTRANSLATED] s to Hacking`,
 | 
					    upgrade_AvatarTimeLimitIncrease: `[UNTRANSLATED] +8s to Hacking`,
 | 
				
			||||||
    upgrade_ElectrifyOnHack: `[UNTRANSLATED] Shock enemies within 20m while 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_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_OnHackLockers: `[UNTRANSLATED] Unlock 5 lockers within 20m after Hacking`,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
// Chinese translation by meb154 & bishan178
 | 
					// Chinese translation by meb154, bishan178 & Corvus
 | 
				
			||||||
dict = {
 | 
					dict = {
 | 
				
			||||||
    general_inventoryUpdateNote: `注意:此处所做的更改只有在游戏同步仓库后才会生效。您可以通过访问星图来触发仓库更新。`,
 | 
					    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_addButton: `添加`,
 | 
				
			||||||
    general_bulkActions: `批量操作`,
 | 
					    general_bulkActions: `批量操作`,
 | 
				
			||||||
    code_loginFail: `登录失败。请检查邮箱和密码。`,
 | 
					    code_loginFail: `登录失败。请检查邮箱和密码。`,
 | 
				
			||||||
@ -74,9 +74,9 @@ dict = {
 | 
				
			|||||||
    inventory_longGuns: `主要武器`,
 | 
					    inventory_longGuns: `主要武器`,
 | 
				
			||||||
    inventory_pistols: `次要武器`,
 | 
					    inventory_pistols: `次要武器`,
 | 
				
			||||||
    inventory_melee: `近战武器`,
 | 
					    inventory_melee: `近战武器`,
 | 
				
			||||||
    inventory_spaceSuits: `Archwings`,
 | 
					    inventory_spaceSuits: `载具`,
 | 
				
			||||||
    inventory_spaceGuns: `Archwing主武器`,
 | 
					    inventory_spaceGuns: `载具主武器`,
 | 
				
			||||||
    inventory_spaceMelee: `Archwing近战武器`,
 | 
					    inventory_spaceMelee: `载具近战武器`,
 | 
				
			||||||
    inventory_mechSuits: `殁世机甲`,
 | 
					    inventory_mechSuits: `殁世机甲`,
 | 
				
			||||||
    inventory_sentinels: `守护`,
 | 
					    inventory_sentinels: `守护`,
 | 
				
			||||||
    inventory_sentinelWeapons: `守护武器`,
 | 
					    inventory_sentinelWeapons: `守护武器`,
 | 
				
			||||||
@ -88,15 +88,15 @@ dict = {
 | 
				
			|||||||
    inventory_Boosters: `加成器`,
 | 
					    inventory_Boosters: `加成器`,
 | 
				
			||||||
    inventory_bulkAddSuits: `添加缺失战甲`,
 | 
					    inventory_bulkAddSuits: `添加缺失战甲`,
 | 
				
			||||||
    inventory_bulkAddWeapons: `添加缺失武器`,
 | 
					    inventory_bulkAddWeapons: `添加缺失武器`,
 | 
				
			||||||
    inventory_bulkAddSpaceSuits: `添加缺失Archwing`,
 | 
					    inventory_bulkAddSpaceSuits: `添加缺失载具`,
 | 
				
			||||||
    inventory_bulkAddSpaceWeapons: `添加缺失Archwing武器`,
 | 
					    inventory_bulkAddSpaceWeapons: `添加缺失载具武器`,
 | 
				
			||||||
    inventory_bulkAddSentinels: `添加缺失守护`,
 | 
					    inventory_bulkAddSentinels: `添加缺失守护`,
 | 
				
			||||||
    inventory_bulkAddSentinelWeapons: `添加缺失守护武器`,
 | 
					    inventory_bulkAddSentinelWeapons: `添加缺失守护武器`,
 | 
				
			||||||
    inventory_bulkAddEvolutionProgress: `添加缺失的灵化之源`,
 | 
					    inventory_bulkAddEvolutionProgress: `添加缺失的灵化之源`,
 | 
				
			||||||
    inventory_bulkRankUpSuits: `所有战甲升满级`,
 | 
					    inventory_bulkRankUpSuits: `所有战甲升满级`,
 | 
				
			||||||
    inventory_bulkRankUpWeapons: `所有武器升满级`,
 | 
					    inventory_bulkRankUpWeapons: `所有武器升满级`,
 | 
				
			||||||
    inventory_bulkRankUpSpaceSuits: `所有Archwing升满级`,
 | 
					    inventory_bulkRankUpSpaceSuits: `所有载具升满级`,
 | 
				
			||||||
    inventory_bulkRankUpSpaceWeapons: `所有Archwing武器升满级`,
 | 
					    inventory_bulkRankUpSpaceWeapons: `所有载具武器升满级`,
 | 
				
			||||||
    inventory_bulkRankUpSentinels: `所有守护升满级`,
 | 
					    inventory_bulkRankUpSentinels: `所有守护升满级`,
 | 
				
			||||||
    inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`,
 | 
					    inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`,
 | 
				
			||||||
    inventory_bulkRankUpEvolutionProgress: `所有灵化之源最大等级`,
 | 
					    inventory_bulkRankUpEvolutionProgress: `所有灵化之源最大等级`,
 | 
				
			||||||
@ -143,7 +143,7 @@ dict = {
 | 
				
			|||||||
    cheats_dontSubtractConsumables: `消耗物品使用时无损耗`,
 | 
					    cheats_dontSubtractConsumables: `消耗物品使用时无损耗`,
 | 
				
			||||||
    cheats_unlockAllShipFeatures: `解锁所有飞船功能`,
 | 
					    cheats_unlockAllShipFeatures: `解锁所有飞船功能`,
 | 
				
			||||||
    cheats_unlockAllShipDecorations: `解锁所有飞船装饰`,
 | 
					    cheats_unlockAllShipDecorations: `解锁所有飞船装饰`,
 | 
				
			||||||
    cheats_unlockAllFlavourItems: `解锁所有<abbr title="动画组合、图标、调色板等">装饰物品</abbr>`,
 | 
					    cheats_unlockAllFlavourItems: `解锁所有<abbr title="动作表情、浮印、调色板等">装饰物品</abbr>`,
 | 
				
			||||||
    cheats_unlockAllSkins: `解锁所有外观`,
 | 
					    cheats_unlockAllSkins: `解锁所有外观`,
 | 
				
			||||||
    cheats_unlockAllCapturaScenes: `解锁所有Captura场景`,
 | 
					    cheats_unlockAllCapturaScenes: `解锁所有Captura场景`,
 | 
				
			||||||
    cheats_unlockAllDecoRecipes: `解锁所有道场配方`,
 | 
					    cheats_unlockAllDecoRecipes: `解锁所有道场配方`,
 | 
				
			||||||
@ -157,8 +157,10 @@ dict = {
 | 
				
			|||||||
    cheats_noMasteryRankUpCooldown: `段位考核无冷却时间`,
 | 
					    cheats_noMasteryRankUpCooldown: `段位考核无冷却时间`,
 | 
				
			||||||
    cheats_noVendorPurchaseLimits: `商城或商人无购买限制`,
 | 
					    cheats_noVendorPurchaseLimits: `商城或商人无购买限制`,
 | 
				
			||||||
    cheats_noDeathMarks: `无死亡标记(不会被 Stalker/Grustrag 三霸/Zanuka 猎人等标记)`,
 | 
					    cheats_noDeathMarks: `无死亡标记(不会被 Stalker/Grustrag 三霸/Zanuka 猎人等标记)`,
 | 
				
			||||||
    cheats_noKimCooldowns: `无 KIM 冷却时间`,
 | 
					    cheats_noKimCooldowns: `即时通无冷却时间`,
 | 
				
			||||||
    cheats_fullyStockedVendors: `[UNTRANSLATED] Fully Stocked Vendors`,
 | 
					    cheats_fullyStockedVendors: `商人贩卖所有商品`,
 | 
				
			||||||
 | 
					    cheats_baroAlwaysAvailable: `虚空商人可永久访问`,
 | 
				
			||||||
 | 
					    cheats_baroFullyStocked: `虚空商人贩卖所有商品`,
 | 
				
			||||||
    cheats_syndicateMissionsRepeatable: `集团任务可重复`,
 | 
					    cheats_syndicateMissionsRepeatable: `集团任务可重复`,
 | 
				
			||||||
    cheats_unlockAllProfitTakerStages: `解锁利润收割者圆蛛所有阶段`,
 | 
					    cheats_unlockAllProfitTakerStages: `解锁利润收割者圆蛛所有阶段`,
 | 
				
			||||||
    cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`,
 | 
					    cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`,
 | 
				
			||||||
@ -172,19 +174,70 @@ dict = {
 | 
				
			|||||||
    cheats_noDojoResearchTime: `无视道场研究时间`,
 | 
					    cheats_noDojoResearchTime: `无视道场研究时间`,
 | 
				
			||||||
    cheats_fastClanAscension: `快速升级氏族`,
 | 
					    cheats_fastClanAscension: `快速升级氏族`,
 | 
				
			||||||
    cheats_missionsCanGiveAllRelics: `任务可获取所有遗物`,
 | 
					    cheats_missionsCanGiveAllRelics: `任务可获取所有遗物`,
 | 
				
			||||||
    cheats_unlockAllSimarisResearchEntries: `[UNTRANSLATED] Unlock All Simaris Research Entries`,
 | 
					    cheats_unlockAllSimarisResearchEntries: `解锁所有Simaris研究条目`,
 | 
				
			||||||
 | 
					    cheats_disableDailyTribute: `[UNTRANSLATED] Disable Daily Tribute`,
 | 
				
			||||||
    cheats_spoofMasteryRank: `伪造精通段位(-1为禁用)`,
 | 
					    cheats_spoofMasteryRank: `伪造精通段位(-1为禁用)`,
 | 
				
			||||||
    cheats_nightwaveStandingMultiplier: `午夜电波声望倍率`,
 | 
					    cheats_nightwaveStandingMultiplier: `午夜电波声望倍率`,
 | 
				
			||||||
    cheats_save: `保存`,
 | 
					    cheats_save: `保存`,
 | 
				
			||||||
    cheats_account: `账户`,
 | 
					    cheats_account: `账户`,
 | 
				
			||||||
    cheats_unlockAllFocusSchools: `解锁所有专精学派`,
 | 
					    cheats_unlockAllFocusSchools: `解锁所有专精学派`,
 | 
				
			||||||
    cheats_helminthUnlockAll: `完全升级Helminth`,
 | 
					    cheats_helminthUnlockAll: `完全升级Helminth`,
 | 
				
			||||||
 | 
					    cheats_addMissingSubsumedAbilities: `添加Helminth未汲取的战甲技能`,
 | 
				
			||||||
    cheats_intrinsicsUnlockAll: `所有内源之力最大等级`,
 | 
					    cheats_intrinsicsUnlockAll: `所有内源之力最大等级`,
 | 
				
			||||||
    cheats_changeSupportedSyndicate: `支持的集团`,
 | 
					    cheats_changeSupportedSyndicate: `支持的集团`,
 | 
				
			||||||
    cheats_changeButton: `更改`,
 | 
					    cheats_changeButton: `更改`,
 | 
				
			||||||
    cheats_none: `无`,
 | 
					    cheats_none: `无`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    worldState: `世界状态配置`,
 | 
				
			||||||
 | 
					    worldState_creditBoost: `现金加成`,
 | 
				
			||||||
 | 
					    worldState_affinityBoost: `经验加成`,
 | 
				
			||||||
 | 
					    worldState_resourceBoost: `资源加成`,
 | 
				
			||||||
 | 
					    worldState_starDays: `活动:星日`,
 | 
				
			||||||
 | 
					    worldState_galleonOfGhouls: `战术警报:尸鬼的帆船战舰`,
 | 
				
			||||||
 | 
					    disabled: `关闭/取消配置`,
 | 
				
			||||||
 | 
					    worldState_we1: `活动阶段:第一周`,
 | 
				
			||||||
 | 
					    worldState_we2: `活动阶段:第二周`,
 | 
				
			||||||
 | 
					    worldState_we3: `活动阶段:第三周`,
 | 
				
			||||||
 | 
					    worldState_eidolonOverride: `夜灵平原/魔胎之境状态`,
 | 
				
			||||||
 | 
					    worldState_day: `白昼/FASS`,
 | 
				
			||||||
 | 
					    worldState_night: `黑夜/VOME`,
 | 
				
			||||||
 | 
					    worldState_vallisOverride: `奥布山谷状态`,
 | 
				
			||||||
 | 
					    worldState_warm: `温暖`,
 | 
				
			||||||
 | 
					    worldState_cold: `寒冷`,
 | 
				
			||||||
 | 
					    worldState_duviriOverride: `双衍王镜状态`,
 | 
				
			||||||
 | 
					    worldState_joy: `喜悦`,
 | 
				
			||||||
 | 
					    worldState_anger: `愤怒`,
 | 
				
			||||||
 | 
					    worldState_envy: `嫉妒`,
 | 
				
			||||||
 | 
					    worldState_sorrow: `悲伤`,
 | 
				
			||||||
 | 
					    worldState_fear: `恐惧`,
 | 
				
			||||||
 | 
					    worldState_nightwaveOverride: `午夜电波系列`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission13Syndicate: `诺拉的混选VOL.9`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission12Syndicate: `诺拉的混选VOL.8`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission11Syndicate: `诺拉的混选VOL.7`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission10Syndicate: `诺拉的混选VOL.6`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission9Syndicate: `诺拉的混选VOL.5`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission8Syndicate: `诺拉的混选VOL.4`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission7Syndicate: `诺拉的混选VOL.3`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission6Syndicate: `诺拉的混选VOL.2`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission5Syndicate: `诺拉的混选VOL.1`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission4Syndicate: `诺拉的精选`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission3Syndicate: `间歇III`,
 | 
				
			||||||
 | 
					    worldState_RadioLegion3Syndicate: `系列3 — 玻璃匠`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermission2Syndicate: `间歇II`,
 | 
				
			||||||
 | 
					    worldState_RadioLegion2Syndicate: `系列2 — 使徒`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionIntermissionSyndicate: `间歇I`,
 | 
				
			||||||
 | 
					    worldState_RadioLegionSyndicate: `系列1 — 土星六号之狼`,
 | 
				
			||||||
 | 
					    worldState_fissures: `虚空裂缝难度设定`,
 | 
				
			||||||
 | 
					    normal: `正常`,
 | 
				
			||||||
 | 
					    worldState_allAtOnceNormal: `全部开启(普通)`,
 | 
				
			||||||
 | 
					    worldState_allAtOnceSteelPath: `全部开启(钢铁之路)`,
 | 
				
			||||||
 | 
					    worldState_theCircuitOverride: `无尽回廊任务循环配置:`,
 | 
				
			||||||
 | 
					    worldState_darvoStockMultiplier: `[UNTRANSLATED] Darvo Stock Multiplier`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import_importNote: `您可以在此处提供完整或部分库存响应(客户端表示)。支持的所有字段<b>将被覆盖</b>到您的账户中。`,
 | 
					    import_importNote: `您可以在此处提供完整或部分库存响应(客户端表示)。支持的所有字段<b>将被覆盖</b>到您的账户中。`,
 | 
				
			||||||
    import_submit: `提交`,
 | 
					    import_submit: `提交`,
 | 
				
			||||||
 | 
					    import_samples: `示例:`,
 | 
				
			||||||
 | 
					    import_samples_maxFocus: `所有专精学派完全精通`,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    upgrade_Equilibrium: `+|VAL|% 能量 来自生命球, +|VAL|% 生命 来自能量球`,
 | 
					    upgrade_Equilibrium: `+|VAL|% 能量 来自生命球, +|VAL|% 生命 来自能量球`,
 | 
				
			||||||
    upgrade_MeleeCritDamage: `+|VAL|% 近战暴击伤害`,
 | 
					    upgrade_MeleeCritDamage: `+|VAL|% 近战暴击伤害`,
 | 
				
			||||||
@ -227,7 +280,7 @@ dict = {
 | 
				
			|||||||
    upgrade_DamageReductionOnHack: `入侵时,+75% 伤害减免`,
 | 
					    upgrade_DamageReductionOnHack: `入侵时,+75% 伤害减免`,
 | 
				
			||||||
    upgrade_OnExecutionReviveCompanion: `怜悯之击 减少同伴复苏时间 15秒`,
 | 
					    upgrade_OnExecutionReviveCompanion: `怜悯之击 减少同伴复苏时间 15秒`,
 | 
				
			||||||
    upgrade_OnExecutionParkourSpeed: `怜悯之击 15秒内 +60% 跑酷速度`,
 | 
					    upgrade_OnExecutionParkourSpeed: `怜悯之击 15秒内 +60% 跑酷速度`,
 | 
				
			||||||
    upgrade_AvatarTimeLimitIncrease: `增加入侵限制时间`,
 | 
					    upgrade_AvatarTimeLimitIncrease: `+8秒 入侵时间`,
 | 
				
			||||||
    upgrade_ElectrifyOnHack: `入侵时震慑20米之内的敌人`,
 | 
					    upgrade_ElectrifyOnHack: `入侵时震慑20米之内的敌人`,
 | 
				
			||||||
    upgrade_OnExecutionTerrify: `怜悯之击 50% 几率让 15米 以内的敌人恐慌`,
 | 
					    upgrade_OnExecutionTerrify: `怜悯之击 50% 几率让 15米 以内的敌人恐慌`,
 | 
				
			||||||
    upgrade_OnHackLockers: `入侵后解锁20米内的5个储物柜`,
 | 
					    upgrade_OnHackLockers: `入侵后解锁20米内的5个储物柜`,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user