forked from OpenWF/SpaceNinjaServer
Compare commits
251 Commits
Author | SHA1 | Date | |
---|---|---|---|
7d02906656 | |||
ed54e00a03 | |||
3d6c880c96 | |||
660768b53b | |||
3de68e51d5 | |||
c06abded11 | |||
9468768947 | |||
0af7f41201 | |||
de1e2a25f2 | |||
1cf7b41d3f | |||
ab9cc685eb | |||
743b784754 | |||
5df533a7fb | |||
9417aa3c84 | |||
a1872e2b07 | |||
9042e85355 | |||
66ee550ccd | |||
7a295a86ec | |||
88d00eaaa1 | |||
1e8f2fc766 | |||
0d842ade90 | |||
4e3a2e17ee | |||
61864b2be1 | |||
45748fa8be | |||
afec59e8a6 | |||
ee1a49f5f2 | |||
9e94083875 | |||
db0e0d80dd | |||
5cda2e2d08 | |||
e23d865044 | |||
c7658b5b20 | |||
9993500eca | |||
267357871b | |||
cf5ed0442d | |||
de36e2ee8d | |||
ca1b6c31b6 | |||
d66c474bfc | |||
781f01520f | |||
ac37702468 | |||
75c011e3cb | |||
4d4f885c8e | |||
66d1a65e63 | |||
48eefd8db1 | |||
4a6a5ea9cc | |||
95c0ad7892 | |||
a90d3a5156 | |||
d0c9409a2d | |||
bbde7b2141 | |||
5271123090 | |||
f3e56480e5 | |||
6f46ace40c | |||
883426e429 | |||
13432bf034 | |||
a1267e5f64 | |||
2058207b6a | |||
c7c416c100 | |||
90e97d7888 | |||
3f6734ac1c | |||
6f64690b91 | |||
fb5a7320bb | |||
143b358a03 | |||
0b75757277 | |||
fd7f4c9e92 | |||
fa6fac494b | |||
6b3f524574 | |||
506365f97e | |||
70646160c3 | |||
3ffa4a7fd3 | |||
826a09a473 | |||
100aefcee4 | |||
409c089d11 | |||
8c32dc2670 | |||
a67f99b665 | |||
756a01d270 | |||
efc7467a99 | |||
99e1a66da8 | |||
370f8c1008 | |||
f039998d71 | |||
eb594af9d8 | |||
bb8596fa87 | |||
a85539a686 | |||
ada6a4bad0 | |||
948104a9a6 | |||
7a8b12b372 | |||
26d644a982 | |||
d6750cd84b | |||
f3601ec43e | |||
15aaa28a4f | |||
ce5b0fc9e2 | |||
64290b72c0 | |||
570c6fe0d1 | |||
146dbd1b89 | |||
e17d43dcb6 | |||
daacbf6f7b | |||
32bb6d4ccb | |||
23dafb53d1 | |||
3aa853f953 | |||
409f41d3bf | |||
c4b8a71c5a | |||
3b20a109f6 | |||
6d93ae9f2d | |||
ad2f143f15 | |||
03590c7360 | |||
e3fca682d6 | |||
c94bc3ef90 | |||
731be0d5e3 | |||
a49edefbd1 | |||
e3a34399e5 | |||
ec6729db4d | |||
72b28f1d75 | |||
bdf0ac722b | |||
98aebba677 | |||
9912a623b1 | |||
98975edca1 | |||
218df461e1 | |||
86d871537b | |||
11f2ffe64d | |||
8fd7152c41 | |||
0f3d9f6c2c | |||
c2a633b549 | |||
7040d422a2 | |||
ba1380ec4c | |||
26f37f58e5 | |||
e59bdcdfbc | |||
c1ca303310 | |||
8afb515231 | |||
5eecf11b1a | |||
37ac10acd2 | |||
8b0ba0b84a | |||
cdead6fdf8 | |||
da6067ec43 | |||
a98e18d511 | |||
6394adb0f0 | |||
bc5dc02fc9 | |||
de5fd5fce0 | |||
a6d4fab595 | |||
f549b042d6 | |||
0c34c87d75 | |||
3baf6ad015 | |||
196182f9a8 | |||
379f57be2c | |||
0d8f5ee66c | |||
79492efbb4 | |||
decbbdc81b | |||
41d976d362 | |||
f94ecbfbfc | |||
f4f1e11b31 | |||
e38d52fb1b | |||
419096f603 | |||
76a53bb1f6 | |||
435aafeaae | |||
8a1603a661 | |||
66e34b7be9 | |||
9940024a01 | |||
ed217bae33 | |||
16e850e7ee | |||
850a073594 | |||
66dae6d3f8 | |||
9a50c05205 | |||
379e83a764 | |||
44da0eb50a | |||
deb652ab37 | |||
0ea67ea89a | |||
51b82df5fd | |||
3d1b009bdb | |||
46aef2c00e | |||
7d607b7348 | |||
4cb1ea94e5 | |||
729061951f | |||
38502f10bf | |||
a738dbfa9a | |||
95562a97ad | |||
c13615c4df | |||
eb6b1c1f57 | |||
64fbdf6064 | |||
47551e93b3 | |||
7a53363b1b | |||
ea0ca8c88b | |||
a10c3b061a | |||
3165d9f459 | |||
28d7ca8ca0 | |||
3f0a2bec48 | |||
d28437b658 | |||
a6d2c8b18a | |||
0c884576bd | |||
380f0662a4 | |||
bd83738168 | |||
fa68a1357d | |||
43f3917b09 | |||
8ebb749732 | |||
827ea47468 | |||
c64d466ce1 | |||
c8ae3d688f | |||
4a971841a1 | |||
9472f855b6 | |||
7736a2bf65 | |||
bef3aeed72 | |||
aacd089123 | |||
d281e929ae | |||
729ea0abff | |||
a75e0c59af | |||
b429eed46c | |||
92d2616dda | |||
37ccd33d5c | |||
20326fdaa0 | |||
6a97a0c7c8 | |||
0928b842ad | |||
e0200b2111 | |||
18a13911ba | |||
2c53d17489 | |||
2eb28c4e89 | |||
2187d9cd7e | |||
e5e6f7963b | |||
900c6e9a26 | |||
f0ee1e8aad | |||
5c6b4b5779 | |||
9b330ffd3e | |||
97d27e8110 | |||
525e3067c9 | |||
0c1fa05e9c | |||
946f3129b8 | |||
355de3fa04 | |||
61e168e444 | |||
70fa48ab07 | |||
dde95c2b61 | |||
2ca79ef898 | |||
63e3c96671 | |||
f0351489be | |||
5149d0e382 | |||
ec8982a921 | |||
cc338c2173 | |||
85b5bb438e | |||
9f727789ca | |||
b308b91f44 | |||
fc3ef3a126 | |||
3f47f89b56 | |||
e784b2dfb8 | |||
c0947b8822 | |||
a0b61bec12 | |||
d3620c00e2 | |||
0ffcee5faf | |||
c2ed8b40f0 | |||
feb1dd4715 | |||
540961ff9e | |||
5692a6201e | |||
541b8d32a8 | |||
74f9d1567f | |||
02a4d2b30a | |||
a8f174bce1 | |||
db1dd21924 | |||
005350bde0 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1,4 +1,4 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
# Auto detect text files and perform LF normalization
|
||||||
* text=auto
|
* text=auto eol=lf
|
||||||
|
|
||||||
static/webui/libs/ linguist-vendored
|
static/webui/libs/ linguist-vendored
|
||||||
|
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@ -5,17 +5,22 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
version: [18, 20, 22]
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.2
|
uses: actions/checkout@v4.1.2
|
||||||
- name: Setup Node.js environment
|
- name: Setup Node.js environment
|
||||||
uses: actions/setup-node@v4.0.2
|
uses: actions/setup-node@v4.0.2
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.version }}
|
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: cp config.json.example config.json
|
- run: cp config.json.example config.json
|
||||||
- run: npm run verify
|
- run: npm run verify
|
||||||
- run: npm run lint
|
- run: npm run lint:ci
|
||||||
|
- run: npm run prettier
|
||||||
|
- run: npm run update-translations
|
||||||
|
- name: Fail if there are uncommitted changes
|
||||||
|
run: |
|
||||||
|
if [[ -n "$(git status --porcelain)" ]]; then
|
||||||
|
echo "Uncommitted changes detected:"
|
||||||
|
git status
|
||||||
|
git --no-pager diff
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
@ -10,6 +10,8 @@ To get an idea of what functionality you can expect to be missing [have a look t
|
|||||||
|
|
||||||
## config.json
|
## config.json
|
||||||
|
|
||||||
|
SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [config.json.example](config.json.example), which has most cheats disabled.
|
||||||
|
|
||||||
- `logger.level` can be `fatal`, `error`, `warn`, `info`, `http`, `debug`, or `trace`.
|
- `logger.level` can be `fatal`, `error`, `warn`, `info`, `http`, `debug`, or `trace`.
|
||||||
- `myIrcAddresses` can be used to point to an IRC server. If not provided, defaults to `[ myAddress ]`.
|
- `myIrcAddresses` can be used to point to an IRC server. If not provided, defaults to `[ myAddress ]`.
|
||||||
- `worldState.lockTime` will lock the time provided in worldState if nonzero, e.g. `1743202800` for night in POE.
|
- `worldState.lockTime` will lock the time provided in worldState if nonzero, e.g. `1743202800` for night in POE.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
echo Updating SpaceNinjaServer...
|
echo Updating SpaceNinjaServer...
|
||||||
git config remote.origin.url https://openwf.io/SpaceNinjaServer.git
|
|
||||||
git fetch --prune
|
git fetch --prune
|
||||||
git stash
|
git stash
|
||||||
git reset --hard origin/main
|
git reset --hard origin/main
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"infiniteEndo": false,
|
"infiniteEndo": false,
|
||||||
"infiniteRegalAya": false,
|
"infiniteRegalAya": false,
|
||||||
"infiniteHelminthMaterials": false,
|
"infiniteHelminthMaterials": false,
|
||||||
|
"dontSubtractConsumables": false,
|
||||||
"unlockAllShipFeatures": false,
|
"unlockAllShipFeatures": false,
|
||||||
"unlockAllShipDecorations": false,
|
"unlockAllShipDecorations": false,
|
||||||
"unlockAllFlavourItems": false,
|
"unlockAllFlavourItems": false,
|
||||||
@ -29,11 +30,17 @@
|
|||||||
"unlockExilusEverywhere": false,
|
"unlockExilusEverywhere": false,
|
||||||
"unlockArcanesEverywhere": false,
|
"unlockArcanesEverywhere": false,
|
||||||
"noDailyStandingLimits": false,
|
"noDailyStandingLimits": false,
|
||||||
|
"noDailyFocusLimit": false,
|
||||||
"noArgonCrystalDecay": false,
|
"noArgonCrystalDecay": false,
|
||||||
"noMasteryRankUpCooldown": false,
|
"noMasteryRankUpCooldown": false,
|
||||||
"noVendorPurchaseLimits": true,
|
"noVendorPurchaseLimits": true,
|
||||||
|
"noDeathMarks": false,
|
||||||
|
"noKimCooldowns": false,
|
||||||
"instantResourceExtractorDrones": false,
|
"instantResourceExtractorDrones": false,
|
||||||
|
"noResourceExtractorDronesDamage": false,
|
||||||
|
"skipClanKeyCrafting": false,
|
||||||
"noDojoRoomBuildStage": false,
|
"noDojoRoomBuildStage": false,
|
||||||
|
"noDecoBuildStage": false,
|
||||||
"fastDojoRoomDestruction": false,
|
"fastDojoRoomDestruction": false,
|
||||||
"noDojoResearchCosts": false,
|
"noDojoResearchCosts": false,
|
||||||
"noDojoResearchTime": false,
|
"noDojoResearchTime": false,
|
||||||
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -17,8 +17,8 @@
|
|||||||
"mongoose": "^8.11.0",
|
"mongoose": "^8.11.0",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"typescript": ">=5.5 <5.6.0",
|
"typescript": "^5.5",
|
||||||
"warframe-public-export-plus": "^0.5.52",
|
"warframe-public-export-plus": "^0.5.59",
|
||||||
"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"
|
||||||
@ -3720,9 +3720,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.5.4",
|
"version": "5.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@ -3789,9 +3789,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/warframe-public-export-plus": {
|
"node_modules/warframe-public-export-plus": {
|
||||||
"version": "0.5.52",
|
"version": "0.5.59",
|
||||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.52.tgz",
|
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.59.tgz",
|
||||||
"integrity": "sha512-mJyQbTFMDwgBSkhUYJzcfJg9qrMTrL1pyZuAxV/Dov68xUikK5zigQSYM3ZkKYbhwBtg0Bx/+7q9GAmPzGaRhA=="
|
"integrity": "sha512-/SUCVjngVDBz6gahz7CdVLywtHLODL6O5nmNtQcxFDUwrUGnF1lETcG8/UO+WLeGxBVAy4BDPbq+9ZWlYZM4uQ=="
|
||||||
},
|
},
|
||||||
"node_modules/warframe-riven-info": {
|
"node_modules/warframe-riven-info": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
"description": "WF Emulator",
|
"description": "WF Emulator",
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --import ./build/src/pathman.js build/src/index.js",
|
"start": "node --enable-source-maps --import ./build/src/pathman.js build/src/index.js",
|
||||||
"dev": "ts-node-dev --openssl-legacy-provider -r tsconfig-paths/register src/index.ts ",
|
"dev": "ts-node-dev --openssl-legacy-provider -r tsconfig-paths/register src/index.ts ",
|
||||||
"build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
|
"build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
|
||||||
"verify": "tsgo --noEmit",
|
"verify": "tsgo --noEmit",
|
||||||
"lint": "eslint --ext .ts .",
|
"lint": "eslint --ext .ts .",
|
||||||
|
"lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
|
||||||
"lint:fix": "eslint --fix --ext .ts .",
|
"lint:fix": "eslint --fix --ext .ts .",
|
||||||
"prettier": "prettier --write .",
|
"prettier": "prettier --write .",
|
||||||
"update-translations": "cd scripts && node update-translations.js"
|
"update-translations": "cd scripts && node update-translations.js"
|
||||||
@ -23,8 +24,8 @@
|
|||||||
"mongoose": "^8.11.0",
|
"mongoose": "^8.11.0",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"typescript": ">=5.5 <5.6.0",
|
"typescript": "^5.5",
|
||||||
"warframe-public-export-plus": "^0.5.52",
|
"warframe-public-export-plus": "^0.5.59",
|
||||||
"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"
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
|
||||||
function extractStrings(content) {
|
function extractStrings(content) {
|
||||||
const regex = /([a-zA-Z_]+): `([^`]*)`,/g;
|
const regex = /([a-zA-Z0-9_]+): `([^`]*)`,/g;
|
||||||
let matches;
|
let matches;
|
||||||
const strings = {};
|
const strings = {};
|
||||||
while ((matches = regex.exec(content)) !== null) {
|
while ((matches = regex.exec(content)) !== null) {
|
||||||
@ -15,7 +15,7 @@ function extractStrings(content) {
|
|||||||
|
|
||||||
const source = fs.readFileSync("../static/webui/translations/en.js", "utf8");
|
const source = fs.readFileSync("../static/webui/translations/en.js", "utf8");
|
||||||
const sourceStrings = extractStrings(source);
|
const sourceStrings = extractStrings(source);
|
||||||
const sourceLines = source.split("\n");
|
const sourceLines = source.substring(0, source.length - 1).split("\n");
|
||||||
|
|
||||||
fs.readdirSync("../static/webui/translations").forEach(file => {
|
fs.readdirSync("../static/webui/translations").forEach(file => {
|
||||||
if (fs.lstatSync(`../static/webui/translations/${file}`).isFile() && file !== "en.js") {
|
if (fs.lstatSync(`../static/webui/translations/${file}`).isFile() && file !== "en.js") {
|
||||||
@ -36,7 +36,7 @@ fs.readdirSync("../static/webui/translations").forEach(file => {
|
|||||||
fs.writeSync(fileHandle, ` ${key}: \`[UNTRANSLATED] ${value}\`,\n`);
|
fs.writeSync(fileHandle, ` ${key}: \`[UNTRANSLATED] ${value}\`,\n`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (line.length) {
|
} else {
|
||||||
fs.writeSync(fileHandle, line + "\n");
|
fs.writeSync(fileHandle, line + "\n");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -16,9 +16,9 @@ import { webuiRouter } from "@/src/routes/webui";
|
|||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use((req, _res, next) => {
|
app.use((req, _res, next) => {
|
||||||
// 38.5.0 introduced "ezip" for encrypted body blobs.
|
// 38.5.0 introduced "ezip" for encrypted body blobs and "e" for request verification only (encrypted body blobs with no application data).
|
||||||
// The bootstrapper decrypts it for us but having an unsupported Content-Encoding here would still be an issue for Express, so removing it.
|
// The bootstrapper decrypts it for us but having an unsupported Content-Encoding here would still be an issue for Express, so removing it.
|
||||||
if (req.headers["content-encoding"] == "ezip") {
|
if (req.headers["content-encoding"] == "ezip" || req.headers["content-encoding"] == "e") {
|
||||||
req.headers["content-encoding"] = undefined;
|
req.headers["content-encoding"] = undefined;
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
@ -26,7 +26,7 @@ app.use((req, _res, next) => {
|
|||||||
|
|
||||||
app.use(bodyParser.raw());
|
app.use(bodyParser.raw());
|
||||||
app.use(express.json({ limit: "4mb" }));
|
app.use(express.json({ limit: "4mb" }));
|
||||||
app.use(bodyParser.text());
|
app.use(bodyParser.text({ limit: "4mb" }));
|
||||||
app.use(requestLogger);
|
app.use(requestLogger);
|
||||||
|
|
||||||
app.use("/api", apiRouter);
|
app.use("/api", apiRouter);
|
||||||
|
@ -2,15 +2,18 @@ const millisecondsPerSecond = 1000;
|
|||||||
const secondsPerMinute = 60;
|
const secondsPerMinute = 60;
|
||||||
const minutesPerHour = 60;
|
const minutesPerHour = 60;
|
||||||
const hoursPerDay = 24;
|
const hoursPerDay = 24;
|
||||||
|
const daysPerWeek = 7;
|
||||||
|
|
||||||
const unixSecond = millisecondsPerSecond;
|
const unixSecond = millisecondsPerSecond;
|
||||||
const unixMinute = secondsPerMinute * millisecondsPerSecond;
|
const unixMinute = secondsPerMinute * millisecondsPerSecond;
|
||||||
const unixHour = unixMinute * minutesPerHour;
|
const unixHour = unixMinute * minutesPerHour;
|
||||||
const unixDay = hoursPerDay * unixHour;
|
const unixDay = hoursPerDay * unixHour;
|
||||||
|
const unixWeek = daysPerWeek * unixDay;
|
||||||
|
|
||||||
export const unixTimesInMs = {
|
export const unixTimesInMs = {
|
||||||
second: unixSecond,
|
second: unixSecond,
|
||||||
minute: unixMinute,
|
minute: unixMinute,
|
||||||
hour: unixHour,
|
hour: unixHour,
|
||||||
day: unixDay
|
day: unixDay,
|
||||||
|
week: unixWeek
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@ export const activateRandomModController: RequestHandler = async (req, res) => {
|
|||||||
ItemCount: -1
|
ItemCount: -1
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType]);
|
const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType])!;
|
||||||
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
|
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
|
||||||
const upgradeIndex =
|
const upgradeIndex =
|
||||||
inventory.Upgrades.push({
|
inventory.Upgrades.push({
|
||||||
|
30
src/controllers/api/addIgnoredUserController.ts
Normal file
30
src/controllers/api/addIgnoredUserController.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { Account, Ignore } from "@/src/models/loginModel";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { IFriendInfo } from "@/src/types/guildTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const addIgnoredUserController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const data = getJSONfromString<IAddIgnoredUserRequest>(String(req.body));
|
||||||
|
const ignoreeAccount = await Account.findOne(
|
||||||
|
{ DisplayName: data.playerName.substring(0, data.playerName.length - 1) },
|
||||||
|
"_id"
|
||||||
|
);
|
||||||
|
if (ignoreeAccount) {
|
||||||
|
await Ignore.create({ ignorer: accountId, ignoree: ignoreeAccount._id });
|
||||||
|
res.json({
|
||||||
|
Ignored: {
|
||||||
|
_id: toOid(ignoreeAccount._id),
|
||||||
|
DisplayName: data.playerName
|
||||||
|
} satisfies IFriendInfo
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.status(400).end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IAddIgnoredUserRequest {
|
||||||
|
playerName: string;
|
||||||
|
}
|
@ -28,7 +28,7 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
|||||||
});
|
});
|
||||||
|
|
||||||
const rawRivenType = getRandomRawRivenType();
|
const rawRivenType = getRandomRawRivenType();
|
||||||
const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType]);
|
const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType])!;
|
||||||
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
|
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
|
||||||
|
|
||||||
const upgradeIndex =
|
const upgradeIndex =
|
||||||
@ -57,12 +57,16 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
|||||||
payload.Consumed.forEach(upgrade => {
|
payload.Consumed.forEach(upgrade => {
|
||||||
const meta = ExportUpgrades[upgrade.ItemType];
|
const meta = ExportUpgrades[upgrade.ItemType];
|
||||||
counts[meta.rarity] += upgrade.ItemCount;
|
counts[meta.rarity] += upgrade.ItemCount;
|
||||||
addMods(inventory, [
|
if (upgrade.ItemId.$oid != "000000000000000000000000") {
|
||||||
{
|
inventory.Upgrades.pull({ _id: upgrade.ItemId.$oid });
|
||||||
ItemType: upgrade.ItemType,
|
} else {
|
||||||
ItemCount: upgrade.ItemCount * -1
|
addMods(inventory, [
|
||||||
}
|
{
|
||||||
]);
|
ItemType: upgrade.ItemType,
|
||||||
|
ItemCount: upgrade.ItemCount * -1
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
if (upgrade.ItemType == "/Lotus/Upgrades/Mods/TransmuteCores/AttackTransmuteCore") {
|
if (upgrade.ItemType == "/Lotus/Upgrades/Mods/TransmuteCores/AttackTransmuteCore") {
|
||||||
forcedPolarity = "AP_ATTACK";
|
forcedPolarity = "AP_ATTACK";
|
||||||
} else if (upgrade.ItemType == "/Lotus/Upgrades/Mods/TransmuteCores/DefenseTransmuteCore") {
|
} else if (upgrade.ItemType == "/Lotus/Upgrades/Mods/TransmuteCores/DefenseTransmuteCore") {
|
||||||
@ -72,22 +76,33 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Based on the table on https://wiki.warframe.com/w/Transmutation
|
let newModType: string | undefined;
|
||||||
const weights: Record<TRarity, number> = {
|
for (const specialModSet of specialModSets) {
|
||||||
COMMON: counts.COMMON * 95 + counts.UNCOMMON * 15 + counts.RARE * 4,
|
if (specialModSet.indexOf(payload.Consumed[0].ItemType) != -1) {
|
||||||
UNCOMMON: counts.COMMON * 4 + counts.UNCOMMON * 80 + counts.RARE * 10,
|
newModType = getRandomElement(specialModSet);
|
||||||
RARE: counts.COMMON * 1 + counts.UNCOMMON * 5 + counts.RARE * 50,
|
break;
|
||||||
LEGENDARY: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const options: { uniqueName: string; rarity: TRarity }[] = [];
|
|
||||||
Object.entries(ExportUpgrades).forEach(([uniqueName, upgrade]) => {
|
|
||||||
if (upgrade.canBeTransmutation && (!forcedPolarity || upgrade.polarity == forcedPolarity)) {
|
|
||||||
options.push({ uniqueName, rarity: upgrade.rarity });
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (!newModType) {
|
||||||
|
// Based on the table on https://wiki.warframe.com/w/Transmutation
|
||||||
|
const weights: Record<TRarity, number> = {
|
||||||
|
COMMON: counts.COMMON * 95 + counts.UNCOMMON * 15 + counts.RARE * 4,
|
||||||
|
UNCOMMON: counts.COMMON * 4 + counts.UNCOMMON * 80 + counts.RARE * 10,
|
||||||
|
RARE: counts.COMMON * 1 + counts.UNCOMMON * 5 + counts.RARE * 50,
|
||||||
|
LEGENDARY: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const options: { uniqueName: string; rarity: TRarity }[] = [];
|
||||||
|
Object.entries(ExportUpgrades).forEach(([uniqueName, upgrade]) => {
|
||||||
|
if (upgrade.canBeTransmutation && (!forcedPolarity || upgrade.polarity == forcedPolarity)) {
|
||||||
|
options.push({ uniqueName, rarity: upgrade.rarity });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newModType = getRandomWeightedReward(options, weights)!.uniqueName;
|
||||||
|
}
|
||||||
|
|
||||||
const newModType = getRandomWeightedReward(options, weights)!.uniqueName;
|
|
||||||
addMods(inventory, [
|
addMods(inventory, [
|
||||||
{
|
{
|
||||||
ItemType: newModType,
|
ItemType: newModType,
|
||||||
@ -130,3 +145,34 @@ interface IAgnosticUpgradeClient {
|
|||||||
ItemCount: number;
|
ItemCount: number;
|
||||||
LastAdded: IOid;
|
LastAdded: IOid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const specialModSets: string[][] = [
|
||||||
|
[
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalOneMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalTwoMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalThreeMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalFourMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalFiveMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalSixMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalSevenMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalEightMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalWildcardMod"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusOneMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusTwoMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusThreeMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusFourMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusFiveMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusSixMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusSevenMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusEightMod"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndSpeedOnUseMod",
|
||||||
|
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndWeaponDamageOnUseMod",
|
||||||
|
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusLargeOnSingleUseMod",
|
||||||
|
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusOnUseMod",
|
||||||
|
"/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusSmallOnSingleUseMod"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
@ -15,6 +15,12 @@ export const changeDojoRootController: RequestHandler = async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Example POST body: {"pivot":[0, 0, -64],"components":"{\"670429301ca0a63848ccc467\":{\"R\":[0,0,0],\"P\":[0,3,32]},\"6704254a1ca0a63848ccb33c\":{\"R\":[0,0,0],\"P\":[0,9.25,-32]},\"670429461ca0a63848ccc731\":{\"R\":[-90,0,0],\"P\":[-47.999992370605,3,16]}}"}
|
||||||
|
if (req.body) {
|
||||||
|
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
||||||
|
throw new Error("dojo reparent operation should not need deco repositioning"); // because we always provide SortId
|
||||||
|
}
|
||||||
|
|
||||||
const idToNode: Record<string, INode> = {};
|
const idToNode: Record<string, INode> = {};
|
||||||
guild.DojoComponents.forEach(x => {
|
guild.DojoComponents.forEach(x => {
|
||||||
idToNode[x._id.toString()] = {
|
idToNode[x._id.toString()] = {
|
||||||
@ -43,23 +49,13 @@ export const changeDojoRootController: RequestHandler = async (req, res) => {
|
|||||||
newRoot.component.pp = undefined;
|
newRoot.component.pp = undefined;
|
||||||
newRoot.parent = undefined;
|
newRoot.parent = undefined;
|
||||||
|
|
||||||
// Don't even ask me why this is needed because I don't know either
|
// Set/update SortId in top-to-bottom order
|
||||||
const stack: INode[] = [newRoot];
|
const stack: INode[] = [newRoot];
|
||||||
let i = 0;
|
|
||||||
const idMap: Record<string, Types.ObjectId> = {};
|
|
||||||
while (stack.length != 0) {
|
while (stack.length != 0) {
|
||||||
const top = stack.shift()!;
|
const top = stack.shift()!;
|
||||||
idMap[top.component._id.toString()] = new Types.ObjectId(
|
top.component.SortId = new Types.ObjectId();
|
||||||
(++i).toString(16).padStart(8, "0") + top.component._id.toString().substr(8)
|
|
||||||
);
|
|
||||||
top.children.forEach(x => stack.push(x));
|
top.children.forEach(x => stack.push(x));
|
||||||
}
|
}
|
||||||
guild.DojoComponents.forEach(x => {
|
|
||||||
x._id = idMap[x._id.toString()];
|
|
||||||
if (x.pi) {
|
|
||||||
x.pi = idMap[x.pi.toString()];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.debug("New tree:\n" + treeToString(newRoot));
|
logger.debug("New tree:\n" + treeToString(newRoot));
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
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";
|
||||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
|
|
||||||
interface IClaimCompletedRecipeRequest {
|
interface IClaimCompletedRecipeRequest {
|
||||||
RecipeIds: IOid[];
|
RecipeIds: IOid[];
|
||||||
@ -80,6 +81,7 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
|||||||
} else {
|
} else {
|
||||||
logger.debug("Claiming Recipe", { recipe, pendingRecipe });
|
logger.debug("Claiming Recipe", { recipe, pendingRecipe });
|
||||||
|
|
||||||
|
let BrandedSuits: undefined | IOid[];
|
||||||
if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
|
if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") {
|
||||||
inventory.PendingSpectreLoadouts ??= [];
|
inventory.PendingSpectreLoadouts ??= [];
|
||||||
inventory.SpectreLoadouts ??= [];
|
inventory.SpectreLoadouts ??= [];
|
||||||
@ -104,9 +106,10 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
|||||||
inventory.BrandedSuits!.findIndex(x => x.equals(pendingRecipe.SuitToUnbrand)),
|
inventory.BrandedSuits!.findIndex(x => x.equals(pendingRecipe.SuitToUnbrand)),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
BrandedSuits = [toOid(pendingRecipe.SuitToUnbrand!)];
|
||||||
}
|
}
|
||||||
|
|
||||||
let InventoryChanges = {};
|
let InventoryChanges: IInventoryChanges = {};
|
||||||
if (recipe.consumeOnUse) {
|
if (recipe.consumeOnUse) {
|
||||||
addRecipes(inventory, [
|
addRecipes(inventory, [
|
||||||
{
|
{
|
||||||
@ -130,10 +133,17 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
|||||||
if (recipe.secretIngredientAction != "SIA_UNBRAND") {
|
if (recipe.secretIngredientAction != "SIA_UNBRAND") {
|
||||||
InventoryChanges = {
|
InventoryChanges = {
|
||||||
...InventoryChanges,
|
...InventoryChanges,
|
||||||
...(await addItem(inventory, recipe.resultType, recipe.num, false))
|
...(await addItem(
|
||||||
|
inventory,
|
||||||
|
recipe.resultType,
|
||||||
|
recipe.num,
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
pendingRecipe.TargetFingerprint
|
||||||
|
))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.json({ InventoryChanges });
|
res.json({ InventoryChanges, BrandedSuits });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ export const claimLibraryDailyTaskRewardController: RequestHandler = async (req,
|
|||||||
}
|
}
|
||||||
syndicate.Standing += rewardStanding;
|
syndicate.Standing += rewardStanding;
|
||||||
|
|
||||||
inventory.FusionPoints += 80 * rewardQuantity;
|
addFusionPoints(inventory, 80 * rewardQuantity);
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
|
@ -7,6 +7,8 @@ export const clearDialogueHistoryController: RequestHandler = async (req, res) =
|
|||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
const request = JSON.parse(String(req.body)) as IClearDialogueRequest;
|
const request = JSON.parse(String(req.body)) as IClearDialogueRequest;
|
||||||
if (inventory.DialogueHistory && inventory.DialogueHistory.Dialogues) {
|
if (inventory.DialogueHistory && inventory.DialogueHistory.Dialogues) {
|
||||||
|
inventory.DialogueHistory.Resets ??= 0;
|
||||||
|
inventory.DialogueHistory.Resets += 1;
|
||||||
for (const dialogueName of request.Dialogues) {
|
for (const dialogueName of request.Dialogues) {
|
||||||
const index = inventory.DialogueHistory.Dialogues.findIndex(x => x.DialogueName == dialogueName);
|
const index = inventory.DialogueHistory.Dialogues.findIndex(x => x.DialogueName == dialogueName);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
|
41
src/controllers/api/completeCalendarEventController.ts
Normal file
41
src/controllers/api/completeCalendarEventController.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { getCalendarProgress, getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||||
|
import { getWorldState } from "@/src/services/worldStateService";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
// GET request; query parameters: CompletedEventIdx=0&Iteration=4&Version=19&Season=CST_SUMMER
|
||||||
|
export const completeCalendarEventController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId);
|
||||||
|
const calendarProgress = getCalendarProgress(inventory);
|
||||||
|
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
||||||
|
let inventoryChanges: IInventoryChanges = {};
|
||||||
|
let dayIndex = 0;
|
||||||
|
for (const day of currentSeason.Days) {
|
||||||
|
if (day.events.length == 0 || day.events[0].type != "CET_CHALLENGE") {
|
||||||
|
if (dayIndex == calendarProgress.SeasonProgress.LastCompletedDayIdx) {
|
||||||
|
if (day.events.length != 0) {
|
||||||
|
const selection = day.events[parseInt(req.query.CompletedEventIdx as string)];
|
||||||
|
if (selection.type == "CET_REWARD") {
|
||||||
|
inventoryChanges = (await handleStoreItemAcquisition(selection.reward!, inventory))
|
||||||
|
.InventoryChanges;
|
||||||
|
} else if (selection.type == "CET_UPGRADE") {
|
||||||
|
calendarProgress.YearProgress.Upgrades.push(selection.upgrade!);
|
||||||
|
} else if (selection.type != "CET_PLOT") {
|
||||||
|
throw new Error(`unexpected selection type: ${selection.type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++dayIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges,
|
||||||
|
CalendarProgress: inventory.CalendarProgress
|
||||||
|
});
|
||||||
|
};
|
@ -1,8 +1,14 @@
|
|||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||||
import { Account } from "@/src/models/loginModel";
|
import { Account } from "@/src/models/loginModel";
|
||||||
import { deleteGuild, getGuildClient, hasGuildPermission, removeDojoKeyItems } from "@/src/services/guildService";
|
import {
|
||||||
import { addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
deleteGuild,
|
||||||
|
getGuildClient,
|
||||||
|
giveClanKey,
|
||||||
|
hasGuildPermission,
|
||||||
|
removeDojoKeyItems
|
||||||
|
} from "@/src/services/guildService";
|
||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
|
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||||
import { GuildPermission } from "@/src/types/guildTypes";
|
import { GuildPermission } from "@/src/types/guildTypes";
|
||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
@ -41,14 +47,7 @@ export const confirmGuildInvitationGetController: RequestHandler = async (req, r
|
|||||||
// Update inventory of new member
|
// Update inventory of new member
|
||||||
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
|
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
|
||||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||||
const recipeChanges = [
|
giveClanKey(inventory, inventoryChanges);
|
||||||
{
|
|
||||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
|
||||||
ItemCount: 1
|
|
||||||
}
|
|
||||||
];
|
|
||||||
addRecipes(inventory, recipeChanges);
|
|
||||||
combineInventoryChanges(inventoryChanges, { Recipes: recipeChanges });
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
|
|
||||||
const guild = (await Guild.findById(req.query.clanId as string))!;
|
const guild = (await Guild.findById(req.query.clanId as string))!;
|
||||||
@ -96,14 +95,9 @@ export const confirmGuildInvitationPostController: RequestHandler = async (req,
|
|||||||
await GuildMember.deleteMany({ accountId: guildMember.accountId, status: 1 });
|
await GuildMember.deleteMany({ accountId: guildMember.accountId, status: 1 });
|
||||||
|
|
||||||
// Update inventory of new member
|
// Update inventory of new member
|
||||||
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId Recipes");
|
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId LevelKeys Recipes");
|
||||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||||
addRecipes(inventory, [
|
giveClanKey(inventory);
|
||||||
{
|
|
||||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
|
||||||
ItemCount: 1
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
|
|
||||||
// Add join to clan log
|
// Add join to clan log
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
import { Guild } from "@/src/models/guildModel";
|
||||||
import { config } from "@/src/services/configService";
|
import { checkClanAscensionHasRequiredContributors } from "@/src/services/guildService";
|
||||||
import { createMessage } from "@/src/services/inboxService";
|
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||||
import { getInventory } from "@/src/services/inventoryService";
|
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
@ -31,49 +30,13 @@ export const contributeGuildClassController: RequestHandler = async (req, res) =
|
|||||||
|
|
||||||
guild.CeremonyContributors.push(new Types.ObjectId(accountId));
|
guild.CeremonyContributors.push(new Types.ObjectId(accountId));
|
||||||
|
|
||||||
// Once required contributor count is hit, the class is committed and there's 72 hours to claim endo.
|
await checkClanAscensionHasRequiredContributors(guild);
|
||||||
if (guild.CeremonyContributors.length == payload.RequiredContributors) {
|
|
||||||
guild.Class = guild.CeremonyClass!;
|
|
||||||
guild.CeremonyClass = undefined;
|
|
||||||
guild.CeremonyResetDate = new Date(Date.now() + (config.fastClanAscension ? 5_000 : 72 * 3600_000));
|
|
||||||
if (!config.fastClanAscension) {
|
|
||||||
// Send message to all active guild members
|
|
||||||
const members = await GuildMember.find({ guildId: payload.GuildId, status: 0 }, "accountId");
|
|
||||||
for (const member of members) {
|
|
||||||
// somewhat unfaithful as on live the "msg" is not a loctag, but since we don't have the string, we'll let the client fill it in with "arg".
|
|
||||||
await createMessage(member.accountId, [
|
|
||||||
{
|
|
||||||
sndr: guild.Name,
|
|
||||||
msg: "/Lotus/Language/Clan/Clan_AscensionCeremonyInProgressDetails",
|
|
||||||
arg: [
|
|
||||||
{
|
|
||||||
Key: "RESETDATE",
|
|
||||||
Tag:
|
|
||||||
guild.CeremonyResetDate.getUTCMonth() +
|
|
||||||
"/" +
|
|
||||||
guild.CeremonyResetDate.getUTCDate() +
|
|
||||||
"/" +
|
|
||||||
(guild.CeremonyResetDate.getUTCFullYear() % 100) +
|
|
||||||
" " +
|
|
||||||
guild.CeremonyResetDate.getUTCHours().toString().padStart(2, "0") +
|
|
||||||
":" +
|
|
||||||
guild.CeremonyResetDate.getUTCMinutes().toString().padStart(2, "0")
|
|
||||||
}
|
|
||||||
],
|
|
||||||
sub: "/Lotus/Language/Clan/Clan_AscensionCeremonyInProgress",
|
|
||||||
icon: "/Lotus/Interface/Graphics/ClanTileImages/ClanEnterDojo.png",
|
|
||||||
highPriority: true
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await guild.save();
|
await guild.save();
|
||||||
|
|
||||||
// Either way, endo is given to the contributor.
|
// Either way, endo is given to the contributor.
|
||||||
const inventory = await getInventory(accountId, "FusionPoints");
|
const inventory = await getInventory(accountId, "FusionPoints");
|
||||||
inventory.FusionPoints += guild.CeremonyEndo!;
|
addFusionPoints(inventory, guild.CeremonyEndo!);
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
|
@ -2,8 +2,9 @@ import { RequestHandler } from "express";
|
|||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||||
import { createUniqueClanName, getGuildClient } from "@/src/services/guildService";
|
import { createUniqueClanName, getGuildClient, giveClanKey } from "@/src/services/guildService";
|
||||||
import { addRecipes, getInventory } from "@/src/services/inventoryService";
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
|
||||||
export const createGuildController: RequestHandler = async (req, res) => {
|
export const createGuildController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
@ -26,26 +27,15 @@ export const createGuildController: RequestHandler = async (req, res) => {
|
|||||||
rank: 0
|
rank: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
const inventory = await getInventory(accountId, "GuildId Recipes");
|
const inventory = await getInventory(accountId, "GuildId LevelKeys Recipes");
|
||||||
inventory.GuildId = guild._id;
|
inventory.GuildId = guild._id;
|
||||||
addRecipes(inventory, [
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
{
|
giveClanKey(inventory, inventoryChanges);
|
||||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
|
||||||
ItemCount: 1
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
...(await getGuildClient(guild, accountId)),
|
...(await getGuildClient(guild, accountId)),
|
||||||
InventoryChanges: {
|
InventoryChanges: inventoryChanges
|
||||||
Recipes: [
|
|
||||||
{
|
|
||||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
|
||||||
ItemCount: 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
28
src/controllers/api/crewMembersController.ts
Normal file
28
src/controllers/api/crewMembersController.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { ICrewMemberClient } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { Types } from "mongoose";
|
||||||
|
|
||||||
|
export const crewMembersController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId, "CrewMembers");
|
||||||
|
const data = getJSONfromString<ICrewMembersRequest>(String(req.body));
|
||||||
|
const dbCrewMember = inventory.CrewMembers.id(data.crewMember.ItemId.$oid)!;
|
||||||
|
dbCrewMember.AssignedRole = data.crewMember.AssignedRole;
|
||||||
|
dbCrewMember.SkillEfficiency = data.crewMember.SkillEfficiency;
|
||||||
|
dbCrewMember.WeaponConfigIdx = data.crewMember.WeaponConfigIdx;
|
||||||
|
dbCrewMember.WeaponId = new Types.ObjectId(data.crewMember.WeaponId.$oid);
|
||||||
|
dbCrewMember.Configs = data.crewMember.Configs;
|
||||||
|
dbCrewMember.SecondInCommand = data.crewMember.SecondInCommand;
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
crewMemberId: data.crewMember.ItemId.$oid,
|
||||||
|
NemesisFingerprint: data.crewMember.NemesisFingerprint
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ICrewMembersRequest {
|
||||||
|
crewMember: ICrewMemberClient;
|
||||||
|
}
|
84
src/controllers/api/crewShipIdentifySalvageController.ts
Normal file
84
src/controllers/api/crewShipIdentifySalvageController.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import {
|
||||||
|
addCrewShipSalvagedWeaponSkin,
|
||||||
|
addCrewShipRawSalvage,
|
||||||
|
getInventory,
|
||||||
|
addEquipment
|
||||||
|
} from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { ICrewShipComponentFingerprint, IInnateDamageFingerprint } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { ExportCustoms, ExportRailjackWeapons, ExportUpgrades } from "warframe-public-export-plus";
|
||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
import { getRandomInt } from "@/src/services/rngService";
|
||||||
|
import { IFingerprintStat } from "@/src/helpers/rivenHelper";
|
||||||
|
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
|
|
||||||
|
export const crewShipIdentifySalvageController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(
|
||||||
|
accountId,
|
||||||
|
"CrewShipSalvagedWeaponSkins CrewShipSalvagedWeapons CrewShipRawSalvage"
|
||||||
|
);
|
||||||
|
const payload = getJSONfromString<ICrewShipIdentifySalvageRequest>(String(req.body));
|
||||||
|
|
||||||
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
|
if (payload.ItemType in ExportCustoms) {
|
||||||
|
const meta = ExportCustoms[payload.ItemType];
|
||||||
|
let upgradeFingerprint: ICrewShipComponentFingerprint = { compat: payload.ItemType, buffs: [] };
|
||||||
|
if (meta.subroutines) {
|
||||||
|
upgradeFingerprint = {
|
||||||
|
SubroutineIndex: getRandomInt(0, meta.subroutines.length - 1),
|
||||||
|
...upgradeFingerprint
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (const upgrade of meta.randomisedUpgrades!) {
|
||||||
|
upgradeFingerprint.buffs.push({ Tag: upgrade.tag, Value: Math.trunc(Math.random() * 0x40000000) });
|
||||||
|
}
|
||||||
|
addCrewShipSalvagedWeaponSkin(
|
||||||
|
inventory,
|
||||||
|
payload.ItemType,
|
||||||
|
JSON.stringify(upgradeFingerprint),
|
||||||
|
inventoryChanges
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const meta = ExportRailjackWeapons[payload.ItemType];
|
||||||
|
let defaultOverwrites: Partial<IEquipmentDatabase> | undefined;
|
||||||
|
if (meta.defaultUpgrades?.[0]) {
|
||||||
|
const upgradeType = meta.defaultUpgrades[0].ItemType;
|
||||||
|
const upgradeMeta = ExportUpgrades[upgradeType];
|
||||||
|
const buffs: IFingerprintStat[] = [];
|
||||||
|
for (const buff of upgradeMeta.upgradeEntries!) {
|
||||||
|
buffs.push({
|
||||||
|
Tag: buff.tag,
|
||||||
|
Value: Math.trunc(Math.random() * 0x40000000)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
defaultOverwrites = {
|
||||||
|
UpgradeType: upgradeType,
|
||||||
|
UpgradeFingerprint: JSON.stringify({
|
||||||
|
compat: payload.ItemType,
|
||||||
|
buffs
|
||||||
|
} satisfies IInnateDamageFingerprint)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
addEquipment(inventory, "CrewShipSalvagedWeapons", payload.ItemType, defaultOverwrites, inventoryChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
inventoryChanges.CrewShipRawSalvage = [
|
||||||
|
{
|
||||||
|
ItemType: payload.ItemType,
|
||||||
|
ItemCount: -1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
addCrewShipRawSalvage(inventory, inventoryChanges.CrewShipRawSalvage);
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ICrewShipIdentifySalvageRequest {
|
||||||
|
ItemType: string;
|
||||||
|
}
|
@ -1,5 +1,11 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
// Arbiter Dojo endpoints, not really used by us as we don't provide a ContentURL.
|
||||||
|
|
||||||
export const dojoController: RequestHandler = (_req, res) => {
|
export const dojoController: RequestHandler = (_req, res) => {
|
||||||
res.json("-1"); // Tell client to use authorised request.
|
res.json("-1"); // Tell client to use authorised request.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setDojoURLController: RequestHandler = (_req, res) => {
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
@ -55,7 +55,7 @@ export const dronesController: RequestHandler = async (req, res) => {
|
|||||||
? new Date()
|
? new Date()
|
||||||
: new Date(Date.now() + getRandomInt(3 * 3600 * 1000, 4 * 3600 * 1000));
|
: new Date(Date.now() + getRandomInt(3 * 3600 * 1000, 4 * 3600 * 1000));
|
||||||
drone.PendingDamage =
|
drone.PendingDamage =
|
||||||
Math.random() < system.damageChance
|
!config.noResourceExtractorDronesDamage && Math.random() < system.damageChance
|
||||||
? getRandomInt(system.droneDamage.minValue, system.droneDamage.maxValue)
|
? getRandomInt(system.droneDamage.minValue, system.droneDamage.maxValue)
|
||||||
: 0;
|
: 0;
|
||||||
const resource = getRandomWeightedRewardUc(system.resources, droneMeta.probabilities)!;
|
const resource = getRandomWeightedRewardUc(system.resources, droneMeta.probabilities)!;
|
||||||
|
@ -21,10 +21,12 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res)
|
|||||||
inventory.EntratiVaultCountResetDate = new Date(weekEnd);
|
inventory.EntratiVaultCountResetDate = new Date(weekEnd);
|
||||||
if (inventory.EntratiLabConquestUnlocked) {
|
if (inventory.EntratiLabConquestUnlocked) {
|
||||||
inventory.EntratiLabConquestUnlocked = 0;
|
inventory.EntratiLabConquestUnlocked = 0;
|
||||||
|
inventory.EntratiLabConquestCacheScoreMission = 0;
|
||||||
inventory.EntratiLabConquestActiveFrameVariants = [];
|
inventory.EntratiLabConquestActiveFrameVariants = [];
|
||||||
}
|
}
|
||||||
if (inventory.EchoesHexConquestUnlocked) {
|
if (inventory.EchoesHexConquestUnlocked) {
|
||||||
inventory.EchoesHexConquestUnlocked = 0;
|
inventory.EchoesHexConquestUnlocked = 0;
|
||||||
|
inventory.EchoesHexConquestCacheScoreMission = 0;
|
||||||
inventory.EchoesHexConquestActiveFrameVariants = [];
|
inventory.EchoesHexConquestActiveFrameVariants = [];
|
||||||
inventory.EchoesHexConquestActiveStickers = [];
|
inventory.EchoesHexConquestActiveStickers = [];
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
import { addMiscItems, addStanding, getInventory } from "@/src/services/inventoryService";
|
||||||
import { addMiscItems, getInventory, getStandingLimit, updateStandingLimit } from "@/src/services/inventoryService";
|
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { ExportResources, ExportSyndicates } from "warframe-public-export-plus";
|
import { ExportResources } from "warframe-public-export-plus";
|
||||||
|
|
||||||
export const fishmongerController: RequestHandler = async (req, res) => {
|
export const fishmongerController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
@ -31,32 +30,15 @@ export const fishmongerController: RequestHandler = async (req, res) => {
|
|||||||
miscItemChanges.push({ ItemType: fish.ItemType, ItemCount: fish.ItemCount * -1 });
|
miscItemChanges.push({ ItemType: fish.ItemType, ItemCount: fish.ItemCount * -1 });
|
||||||
}
|
}
|
||||||
addMiscItems(inventory, miscItemChanges);
|
addMiscItems(inventory, miscItemChanges);
|
||||||
if (gainedStanding && syndicateTag) {
|
let affiliationMod;
|
||||||
let syndicate = inventory.Affiliations.find(x => x.Tag == syndicateTag);
|
if (gainedStanding && syndicateTag) affiliationMod = addStanding(inventory, syndicateTag, gainedStanding);
|
||||||
if (!syndicate) {
|
|
||||||
syndicate = inventory.Affiliations[inventory.Affiliations.push({ Tag: syndicateTag, Standing: 0 }) - 1];
|
|
||||||
}
|
|
||||||
const syndicateMeta = ExportSyndicates[syndicateTag];
|
|
||||||
|
|
||||||
const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0);
|
|
||||||
if (syndicate.Standing + gainedStanding > max) {
|
|
||||||
gainedStanding = max - syndicate.Standing;
|
|
||||||
}
|
|
||||||
if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) {
|
|
||||||
gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin);
|
|
||||||
}
|
|
||||||
|
|
||||||
syndicate.Standing += gainedStanding;
|
|
||||||
|
|
||||||
updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding);
|
|
||||||
}
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.json({
|
res.json({
|
||||||
InventoryChanges: {
|
InventoryChanges: {
|
||||||
MiscItems: miscItemChanges
|
MiscItems: miscItemChanges
|
||||||
},
|
},
|
||||||
SyndicateTag: syndicateTag,
|
SyndicateTag: syndicateTag,
|
||||||
StandingChange: gainedStanding
|
StandingChange: affiliationMod?.Standing || 0
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,13 +104,14 @@ export const focusController: RequestHandler = async (req, res) => {
|
|||||||
}
|
}
|
||||||
case FocusOperation.SentTrainingAmplifier: {
|
case FocusOperation.SentTrainingAmplifier: {
|
||||||
const request = JSON.parse(String(req.body)) as ISentTrainingAmplifierRequest;
|
const request = JSON.parse(String(req.body)) as ISentTrainingAmplifierRequest;
|
||||||
const parts: string[] = [
|
|
||||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingGrip",
|
|
||||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis",
|
|
||||||
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
|
|
||||||
];
|
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, parts);
|
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, {
|
||||||
|
ModularParts: [
|
||||||
|
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingGrip",
|
||||||
|
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingChassis",
|
||||||
|
"/Lotus/Weapons/Sentients/OperatorAmplifiers/SentTrainingAmplifier/SentAmpTrainingBarrel"
|
||||||
|
]
|
||||||
|
});
|
||||||
occupySlot(inventory, InventorySlot.AMPS, false);
|
occupySlot(inventory, InventorySlot.AMPS, false);
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]);
|
res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]);
|
||||||
|
84
src/controllers/api/gardeningController.ts
Normal file
84
src/controllers/api/gardeningController.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { addMiscItem, getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { toStoreItem } from "@/src/services/itemDataService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||||
|
import { IMongoDate } from "@/src/types/commonTypes";
|
||||||
|
import { IMissionReward } from "@/src/types/missionTypes";
|
||||||
|
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
import { IGardeningClient } from "@/src/types/shipTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { dict_en, ExportResources } from "warframe-public-export-plus";
|
||||||
|
|
||||||
|
export const gardeningController: RequestHandler = async (req, res) => {
|
||||||
|
const data = getJSONfromString<IGardeningRequest>(String(req.body));
|
||||||
|
if (data.Mode != "HarvestAll") {
|
||||||
|
throw new Error(`unexpected gardening mode: ${data.Mode}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const [inventory, personalRooms] = await Promise.all([
|
||||||
|
getInventory(accountId, "MiscItems"),
|
||||||
|
getPersonalRooms(accountId, "Apartment")
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Harvest plants
|
||||||
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
|
const rewards: Record<string, IMissionReward[][]> = {};
|
||||||
|
for (const planter of personalRooms.Apartment.Gardening.Planters) {
|
||||||
|
rewards[planter.Name] = [];
|
||||||
|
for (const plant of planter.Plants) {
|
||||||
|
const itemType =
|
||||||
|
"/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItem" +
|
||||||
|
plant.PlantType.substring(plant.PlantType.length - 1);
|
||||||
|
const itemCount = Math.random() < 0.775 ? 2 : 4;
|
||||||
|
|
||||||
|
addMiscItem(inventory, itemType, itemCount, inventoryChanges);
|
||||||
|
|
||||||
|
rewards[planter.Name].push([
|
||||||
|
{
|
||||||
|
StoreItem: toStoreItem(itemType),
|
||||||
|
TypeName: itemType,
|
||||||
|
ItemCount: itemCount,
|
||||||
|
DailyCooldown: false,
|
||||||
|
Rarity: itemCount == 2 ? 0.7743589743589744 : 0.22564102564102564,
|
||||||
|
TweetText: `${itemCount}x ${dict_en[ExportResources[itemType].name]} (Resource)`,
|
||||||
|
ProductCategory: "MiscItems"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh garden
|
||||||
|
personalRooms.Apartment.Gardening = createGarden();
|
||||||
|
|
||||||
|
await Promise.all([inventory.save(), personalRooms.save()]);
|
||||||
|
|
||||||
|
const planter = personalRooms.Apartment.Gardening.Planters[personalRooms.Apartment.Gardening.Planters.length - 1];
|
||||||
|
const plant = planter.Plants[planter.Plants.length - 1];
|
||||||
|
res.json({
|
||||||
|
GardenTagName: planter.Name,
|
||||||
|
PlantType: plant.PlantType,
|
||||||
|
PlotIndex: plant.PlotIndex,
|
||||||
|
EndTime: toMongoDate(plant.EndTime),
|
||||||
|
InventoryChanges: inventoryChanges,
|
||||||
|
Gardening: personalRooms.toJSON<IPersonalRoomsClient>().Apartment.Gardening,
|
||||||
|
Rewards: rewards
|
||||||
|
} satisfies IGardeningResponse);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IGardeningRequest {
|
||||||
|
Mode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IGardeningResponse {
|
||||||
|
GardenTagName: string;
|
||||||
|
PlantType: string;
|
||||||
|
PlotIndex: number;
|
||||||
|
EndTime: IMongoDate;
|
||||||
|
InventoryChanges: IInventoryChanges;
|
||||||
|
Gardening: IGardeningClient;
|
||||||
|
Rewards: Record<string, IMissionReward[][]>;
|
||||||
|
}
|
@ -1,16 +1,20 @@
|
|||||||
|
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
|
import { Account, Ignore } from "@/src/models/loginModel";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { IFriendInfo } from "@/src/types/guildTypes";
|
||||||
|
import { parallelForeach } from "@/src/utils/async-utils";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
const getIgnoredUsersController: RequestHandler = (_req, res) => {
|
export const getIgnoredUsersController: RequestHandler = async (req, res) => {
|
||||||
res.writeHead(200, {
|
const accountId = await getAccountIdForRequest(req);
|
||||||
"Content-Type": "text/html",
|
const ignores = await Ignore.find({ ignorer: accountId });
|
||||||
"Content-Length": "3"
|
const ignoredUsers: IFriendInfo[] = [];
|
||||||
|
await parallelForeach(ignores, async ignore => {
|
||||||
|
const ignoreeAccount = (await Account.findById(ignore.ignoree, "DisplayName"))!;
|
||||||
|
ignoredUsers.push({
|
||||||
|
_id: toOid(ignore.ignoree),
|
||||||
|
DisplayName: ignoreeAccount.DisplayName + ""
|
||||||
|
});
|
||||||
});
|
});
|
||||||
res.end(
|
res.json({ IgnoredUsers: ignoredUsers });
|
||||||
Buffer.from([
|
|
||||||
0x7b, 0x22, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x3a, 0x38, 0x33, 0x30, 0x34, 0x30, 0x37, 0x37, 0x32, 0x32,
|
|
||||||
0x34, 0x30, 0x32, 0x32, 0x32, 0x36, 0x31, 0x35, 0x30, 0x31, 0x7d
|
|
||||||
])
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getIgnoredUsersController };
|
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
import { generateRewardSeed } from "@/src/services/inventoryService";
|
import { generateRewardSeed } from "@/src/services/inventoryService";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { logger } from "@/src/utils/logger";
|
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
export const getNewRewardSeedController: RequestHandler = async (req, res) => {
|
export const getNewRewardSeedController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
|
||||||
const rewardSeed = generateRewardSeed();
|
const rewardSeed = generateRewardSeed();
|
||||||
logger.debug(`generated new reward seed: ${rewardSeed}`);
|
|
||||||
await Inventory.updateOne(
|
await Inventory.updateOne(
|
||||||
{
|
{
|
||||||
accountOwnerId: accountId
|
accountOwnerId: accountId
|
||||||
|
@ -2,17 +2,24 @@ import { RequestHandler } from "express";
|
|||||||
import { config } from "@/src/services/configService";
|
import { config } from "@/src/services/configService";
|
||||||
import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json";
|
import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||||
import { getShip } from "@/src/services/shipService";
|
import { getShip } from "@/src/services/shipService";
|
||||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
import { IGetShipResponse } from "@/src/types/shipTypes";
|
import { IGetShipResponse } from "@/src/types/shipTypes";
|
||||||
import { IPersonalRooms } from "@/src/types/personalRoomsTypes";
|
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||||
import { getLoadout } from "@/src/services/loadoutService";
|
import { getLoadout } from "@/src/services/loadoutService";
|
||||||
|
|
||||||
export const getShipController: RequestHandler = async (req, res) => {
|
export const getShipController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const personalRoomsDb = await getPersonalRooms(accountId);
|
const personalRoomsDb = await getPersonalRooms(accountId);
|
||||||
const personalRooms = personalRoomsDb.toJSON<IPersonalRooms>();
|
|
||||||
|
// Setup gardening if it's missing. Maybe should be done as part of some quest completion in the future.
|
||||||
|
if (personalRoomsDb.Apartment.Gardening.Planters.length == 0) {
|
||||||
|
personalRoomsDb.Apartment.Gardening = createGarden();
|
||||||
|
await personalRoomsDb.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
const personalRooms = personalRoomsDb.toJSON<IPersonalRoomsClient>();
|
||||||
const loadout = await getLoadout(accountId);
|
const loadout = await getLoadout(accountId);
|
||||||
const ship = await getShip(personalRoomsDb.activeShipId, "ShipAttachments SkinFlavourItem");
|
const ship = await getShip(personalRoomsDb.activeShipId, "ShipAttachments SkinFlavourItem");
|
||||||
|
|
||||||
@ -26,7 +33,10 @@ export const getShipController: RequestHandler = async (req, res) => {
|
|||||||
Colors: personalRooms.ShipInteriorColors,
|
Colors: personalRooms.ShipInteriorColors,
|
||||||
ShipAttachments: ship.ShipAttachments,
|
ShipAttachments: ship.ShipAttachments,
|
||||||
SkinFlavourItem: ship.SkinFlavourItem
|
SkinFlavourItem: ship.SkinFlavourItem
|
||||||
}
|
},
|
||||||
|
FavouriteLoadoutId: personalRooms.Ship.FavouriteLoadoutId
|
||||||
|
? toOid(personalRooms.Ship.FavouriteLoadoutId)
|
||||||
|
: undefined
|
||||||
},
|
},
|
||||||
Apartment: personalRooms.Apartment,
|
Apartment: personalRooms.Apartment,
|
||||||
TailorShop: personalRooms.TailorShop
|
TailorShop: personalRooms.TailorShop
|
||||||
|
@ -2,36 +2,25 @@ import { RequestHandler } from "express";
|
|||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
|
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
|
||||||
import { WeaponTypeInternal } from "@/src/services/itemDataService";
|
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { ArtifactPolarity, EquipmentFeatures, IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
import { ArtifactPolarity, EquipmentFeatures, IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
import { ExportRecipes } from "warframe-public-export-plus";
|
import { ExportRecipes } from "warframe-public-export-plus";
|
||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
|
||||||
const modularWeaponCategory: (WeaponTypeInternal | "Hoverboards")[] = [
|
|
||||||
"LongGuns",
|
|
||||||
"Pistols",
|
|
||||||
"Melee",
|
|
||||||
"OperatorAmps",
|
|
||||||
"Hoverboards"
|
|
||||||
];
|
|
||||||
|
|
||||||
interface IGildWeaponRequest {
|
interface IGildWeaponRequest {
|
||||||
ItemName: string;
|
ItemName: string;
|
||||||
Recipe: string; // e.g. /Lotus/Weapons/SolarisUnited/LotusGildKitgunBlueprint
|
Recipe: string; // e.g. /Lotus/Weapons/SolarisUnited/LotusGildKitgunBlueprint
|
||||||
PolarizeSlot?: number;
|
PolarizeSlot?: number;
|
||||||
PolarizeValue?: ArtifactPolarity;
|
PolarizeValue?: ArtifactPolarity;
|
||||||
ItemId: string;
|
ItemId: string;
|
||||||
Category: WeaponTypeInternal | "Hoverboards";
|
Category: TEquipmentKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const gildWeaponController: RequestHandler = async (req, res) => {
|
export const gildWeaponController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const data = getJSONfromString<IGildWeaponRequest>(String(req.body));
|
const data = getJSONfromString<IGildWeaponRequest>(String(req.body));
|
||||||
data.ItemId = String(req.query.ItemId);
|
data.ItemId = String(req.query.ItemId);
|
||||||
if (!modularWeaponCategory.includes(req.query.Category as WeaponTypeInternal | "Hoverboards")) {
|
data.Category = req.query.Category as TEquipmentKey;
|
||||||
throw new Error(`Unknown modular weapon Category: ${String(req.query.Category)}`);
|
|
||||||
}
|
|
||||||
data.Category = req.query.Category as WeaponTypeInternal | "Hoverboards";
|
|
||||||
|
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
const weaponIndex = inventory[data.Category].findIndex(x => String(x._id) === data.ItemId);
|
const weaponIndex = inventory[data.Category].findIndex(x => String(x._id) === data.ItemId);
|
||||||
@ -42,8 +31,10 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
|||||||
const weapon = inventory[data.Category][weaponIndex];
|
const weapon = inventory[data.Category][weaponIndex];
|
||||||
weapon.Features ??= 0;
|
weapon.Features ??= 0;
|
||||||
weapon.Features |= EquipmentFeatures.GILDED;
|
weapon.Features |= EquipmentFeatures.GILDED;
|
||||||
weapon.ItemName = data.ItemName;
|
if (data.Recipe != "webui") {
|
||||||
weapon.XP = 0;
|
weapon.ItemName = data.ItemName;
|
||||||
|
weapon.XP = 0;
|
||||||
|
}
|
||||||
if (data.Category != "OperatorAmps" && data.PolarizeSlot && data.PolarizeValue) {
|
if (data.Category != "OperatorAmps" && data.PolarizeSlot && data.PolarizeValue) {
|
||||||
weapon.Polarity = [
|
weapon.Polarity = [
|
||||||
{
|
{
|
||||||
@ -56,21 +47,24 @@ export const gildWeaponController: RequestHandler = async (req, res) => {
|
|||||||
const inventoryChanges: IInventoryChanges = {};
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
inventoryChanges[data.Category] = [weapon.toJSON<IEquipmentClient>()];
|
inventoryChanges[data.Category] = [weapon.toJSON<IEquipmentClient>()];
|
||||||
|
|
||||||
const recipe = ExportRecipes[data.Recipe];
|
|
||||||
inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({
|
|
||||||
ItemType: ingredient.ItemType,
|
|
||||||
ItemCount: ingredient.ItemCount * -1
|
|
||||||
}));
|
|
||||||
addMiscItems(inventory, inventoryChanges.MiscItems);
|
|
||||||
|
|
||||||
const affiliationMods = [];
|
const affiliationMods = [];
|
||||||
if (recipe.syndicateStandingChange) {
|
|
||||||
const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!;
|
if (data.Recipe != "webui") {
|
||||||
affiliation.Standing += recipe.syndicateStandingChange.value;
|
const recipe = ExportRecipes[data.Recipe];
|
||||||
affiliationMods.push({
|
inventoryChanges.MiscItems = recipe.secretIngredients!.map(ingredient => ({
|
||||||
Tag: recipe.syndicateStandingChange.tag,
|
ItemType: ingredient.ItemType,
|
||||||
Standing: recipe.syndicateStandingChange.value
|
ItemCount: ingredient.ItemCount * -1
|
||||||
});
|
}));
|
||||||
|
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||||
|
|
||||||
|
if (recipe.syndicateStandingChange) {
|
||||||
|
const affiliation = inventory.Affiliations.find(x => x.Tag == recipe.syndicateStandingChange!.tag)!;
|
||||||
|
affiliation.Standing += recipe.syndicateStandingChange.value;
|
||||||
|
affiliationMods.push({
|
||||||
|
Tag: recipe.syndicateStandingChange.tag,
|
||||||
|
Standing: recipe.syndicateStandingChange.value
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
|
@ -16,7 +16,7 @@ export const giveQuestKeyRewardController: RequestHandler = async (req, res) =>
|
|||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
const inventoryChanges = await addItem(inventory, reward.ItemType, reward.Amount);
|
const inventoryChanges = await addItem(inventory, reward.ItemType, reward.Amount);
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.json(inventoryChanges.InventoryChanges);
|
res.json(inventoryChanges);
|
||||||
//TODO: consider whishlist changes
|
//TODO: consider whishlist changes
|
||||||
};
|
};
|
||||||
|
|
20
src/controllers/api/giveShipDecoAndLoreFragmentController.ts
Normal file
20
src/controllers/api/giveShipDecoAndLoreFragmentController.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { addLoreFragmentScans, addShipDecorations, getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { ILoreFragmentScan, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const giveShipDecoAndLoreFragmentController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId, "LoreFragmentScans ShipDecorations");
|
||||||
|
const data = getJSONfromString<IGiveShipDecoAndLoreFragmentRequest>(String(req.body));
|
||||||
|
addLoreFragmentScans(inventory, data.LoreFragmentScans);
|
||||||
|
addShipDecorations(inventory, data.ShipDecorations);
|
||||||
|
await inventory.save();
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IGiveShipDecoAndLoreFragmentRequest {
|
||||||
|
LoreFragmentScans: ILoreFragmentScan[];
|
||||||
|
ShipDecorations: ITypeCount[];
|
||||||
|
}
|
@ -14,29 +14,33 @@ import {
|
|||||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import {
|
import {
|
||||||
|
addCrewShipWeaponSkin,
|
||||||
|
addEquipment,
|
||||||
addItem,
|
addItem,
|
||||||
addMiscItems,
|
addMiscItems,
|
||||||
addRecipes,
|
addRecipes,
|
||||||
combineInventoryChanges,
|
combineInventoryChanges,
|
||||||
getInventory,
|
getInventory,
|
||||||
|
occupySlot,
|
||||||
updateCurrency
|
updateCurrency
|
||||||
} from "@/src/services/inventoryService";
|
} from "@/src/services/inventoryService";
|
||||||
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { IMiscItem, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
import { config } from "@/src/services/configService";
|
import { config } from "@/src/services/configService";
|
||||||
import { GuildPermission, ITechProjectClient } from "@/src/types/guildTypes";
|
import { GuildPermission, ITechProjectClient } from "@/src/types/guildTypes";
|
||||||
import { GuildMember } from "@/src/models/guildModel";
|
import { GuildMember } from "@/src/models/guildModel";
|
||||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
|
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
|
|
||||||
export const guildTechController: RequestHandler = async (req, res) => {
|
export const guildTechController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
const guild = await getGuildForRequestEx(req, inventory);
|
|
||||||
const data = JSON.parse(String(req.body)) as TGuildTechRequest;
|
const data = JSON.parse(String(req.body)) as TGuildTechRequest;
|
||||||
if (data.Action == "Sync") {
|
if (data.Action == "Sync") {
|
||||||
let needSave = false;
|
let needSave = false;
|
||||||
const techProjects: ITechProjectClient[] = [];
|
const techProjects: ITechProjectClient[] = [];
|
||||||
|
const guild = await getGuildForRequestEx(req, inventory);
|
||||||
if (guild.TechProjects) {
|
if (guild.TechProjects) {
|
||||||
for (const project of guild.TechProjects) {
|
for (const project of guild.TechProjects) {
|
||||||
const techProject: ITechProjectClient = {
|
const techProject: ITechProjectClient = {
|
||||||
@ -59,137 +63,224 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
|||||||
}
|
}
|
||||||
res.json({ TechProjects: techProjects });
|
res.json({ TechProjects: techProjects });
|
||||||
} else if (data.Action == "Start") {
|
} else if (data.Action == "Start") {
|
||||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
if (data.Mode == "Guild") {
|
||||||
res.status(400).send("-1").end();
|
const guild = await getGuildForRequestEx(req, inventory);
|
||||||
return;
|
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
||||||
}
|
res.status(400).send("-1").end();
|
||||||
const recipe = ExportDojoRecipes.research[data.RecipeType];
|
return;
|
||||||
guild.TechProjects ??= [];
|
}
|
||||||
if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) {
|
const recipe = ExportDojoRecipes.research[data.RecipeType];
|
||||||
const techProject =
|
guild.TechProjects ??= [];
|
||||||
guild.TechProjects[
|
if (!guild.TechProjects.find(x => x.ItemType == data.RecipeType)) {
|
||||||
guild.TechProjects.push({
|
const techProject =
|
||||||
ItemType: data.RecipeType,
|
guild.TechProjects[
|
||||||
ReqCredits: config.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, recipe.price),
|
guild.TechProjects.push({
|
||||||
ReqItems: recipe.ingredients.map(x => ({
|
ItemType: data.RecipeType,
|
||||||
ItemType: x.ItemType,
|
ReqCredits: config.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, recipe.price),
|
||||||
ItemCount: config.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, x.ItemCount)
|
ReqItems: recipe.ingredients.map(x => ({
|
||||||
})),
|
ItemType: x.ItemType,
|
||||||
State: 0
|
ItemCount: config.noDojoResearchCosts ? 0 : scaleRequiredCount(guild.Tier, x.ItemCount)
|
||||||
}) - 1
|
})),
|
||||||
];
|
State: 0
|
||||||
setGuildTechLogState(guild, techProject.ItemType, 5);
|
}) - 1
|
||||||
if (config.noDojoResearchCosts) {
|
];
|
||||||
processFundedGuildTechProject(guild, techProject, recipe);
|
setGuildTechLogState(guild, techProject.ItemType, 5);
|
||||||
} else {
|
if (config.noDojoResearchCosts) {
|
||||||
if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
|
processFundedGuildTechProject(guild, techProject, recipe);
|
||||||
guild.ActiveDojoColorResearch = data.RecipeType;
|
} else {
|
||||||
|
if (data.RecipeType.substring(0, 39) == "/Lotus/Types/Items/Research/DojoColors/") {
|
||||||
|
guild.ActiveDojoColorResearch = data.RecipeType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
await guild.save();
|
||||||
await guild.save();
|
res.end();
|
||||||
res.end();
|
} else {
|
||||||
} else if (data.Action == "Contribute") {
|
const recipe = ExportDojoRecipes.research[data.RecipeType];
|
||||||
if (!hasAccessToDojo(inventory)) {
|
if (data.TechProductCategory) {
|
||||||
res.status(400).send("-1").end();
|
if (
|
||||||
return;
|
data.TechProductCategory != "CrewShipWeapons" &&
|
||||||
}
|
data.TechProductCategory != "CrewShipWeaponSkins"
|
||||||
|
) {
|
||||||
const guildMember = (await GuildMember.findOne(
|
throw new Error(`unexpected TechProductCategory: ${data.TechProductCategory}`);
|
||||||
{ accountId, guildId: guild._id },
|
}
|
||||||
"RegularCreditsContributed MiscItemsContributed"
|
if (!inventory[getSalvageCategory(data.TechProductCategory)].id(data.CategoryItemId)) {
|
||||||
))!;
|
throw new Error(
|
||||||
|
`no item with id ${data.CategoryItemId} in ${getSalvageCategory(data.TechProductCategory)} array`
|
||||||
const contributions = data;
|
);
|
||||||
const techProject = guild.TechProjects!.find(x => x.ItemType == contributions.RecipeType)!;
|
}
|
||||||
|
|
||||||
if (contributions.VaultCredits) {
|
|
||||||
if (contributions.VaultCredits > techProject.ReqCredits) {
|
|
||||||
contributions.VaultCredits = techProject.ReqCredits;
|
|
||||||
}
|
}
|
||||||
techProject.ReqCredits -= contributions.VaultCredits;
|
const techProject =
|
||||||
guild.VaultRegularCredits! -= contributions.VaultCredits;
|
inventory.PersonalTechProjects[
|
||||||
|
inventory.PersonalTechProjects.push({
|
||||||
|
State: 0,
|
||||||
|
ReqCredits: recipe.price,
|
||||||
|
ItemType: data.RecipeType,
|
||||||
|
ProductCategory: data.TechProductCategory,
|
||||||
|
CategoryItemId: data.CategoryItemId,
|
||||||
|
ReqItems: recipe.ingredients
|
||||||
|
}) - 1
|
||||||
|
];
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
isPersonal: true,
|
||||||
|
action: "Start",
|
||||||
|
personalTech: techProject.toJSON()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
} else if (data.Action == "Contribute") {
|
||||||
|
if ((req.query.guildId as string) == "000000000000000000000000") {
|
||||||
|
const techProject = inventory.PersonalTechProjects.id(data.ResearchId)!;
|
||||||
|
|
||||||
if (contributions.RegularCredits > techProject.ReqCredits) {
|
techProject.ReqCredits -= data.RegularCredits;
|
||||||
contributions.RegularCredits = techProject.ReqCredits;
|
const inventoryChanges: IInventoryChanges = updateCurrency(inventory, data.RegularCredits, false);
|
||||||
}
|
|
||||||
techProject.ReqCredits -= contributions.RegularCredits;
|
|
||||||
|
|
||||||
guildMember.RegularCreditsContributed ??= 0;
|
const miscItemChanges = [];
|
||||||
guildMember.RegularCreditsContributed += contributions.RegularCredits;
|
for (const miscItem of data.MiscItems) {
|
||||||
|
|
||||||
if (contributions.VaultMiscItems.length) {
|
|
||||||
for (const miscItem of contributions.VaultMiscItems) {
|
|
||||||
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
||||||
if (reqItem) {
|
if (reqItem) {
|
||||||
if (miscItem.ItemCount > reqItem.ItemCount) {
|
if (miscItem.ItemCount > reqItem.ItemCount) {
|
||||||
miscItem.ItemCount = reqItem.ItemCount;
|
miscItem.ItemCount = reqItem.ItemCount;
|
||||||
}
|
}
|
||||||
reqItem.ItemCount -= miscItem.ItemCount;
|
reqItem.ItemCount -= miscItem.ItemCount;
|
||||||
|
miscItemChanges.push({
|
||||||
const vaultMiscItem = guild.VaultMiscItems!.find(x => x.ItemType == miscItem.ItemType)!;
|
ItemType: miscItem.ItemType,
|
||||||
vaultMiscItem.ItemCount -= miscItem.ItemCount;
|
ItemCount: miscItem.ItemCount * -1
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
addMiscItems(inventory, miscItemChanges);
|
||||||
|
inventoryChanges.MiscItems = miscItemChanges;
|
||||||
|
|
||||||
const miscItemChanges = [];
|
techProject.HasContributions = true;
|
||||||
for (const miscItem of contributions.MiscItems) {
|
|
||||||
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
|
||||||
if (reqItem) {
|
|
||||||
if (miscItem.ItemCount > reqItem.ItemCount) {
|
|
||||||
miscItem.ItemCount = reqItem.ItemCount;
|
|
||||||
}
|
|
||||||
reqItem.ItemCount -= miscItem.ItemCount;
|
|
||||||
miscItemChanges.push({
|
|
||||||
ItemType: miscItem.ItemType,
|
|
||||||
ItemCount: miscItem.ItemCount * -1
|
|
||||||
});
|
|
||||||
|
|
||||||
addGuildMemberMiscItemContribution(guildMember, miscItem);
|
if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) {
|
||||||
|
techProject.State = 1;
|
||||||
|
const recipe = ExportDojoRecipes.research[techProject.ItemType];
|
||||||
|
techProject.CompletionDate = new Date(Date.now() + recipe.time * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges,
|
||||||
|
PersonalResearch: { $oid: data.ResearchId },
|
||||||
|
PersonalResearchDate: techProject.CompletionDate ? toMongoDate(techProject.CompletionDate) : undefined
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!hasAccessToDojo(inventory)) {
|
||||||
|
res.status(400).send("-1").end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const guild = await getGuildForRequestEx(req, inventory);
|
||||||
|
const guildMember = (await GuildMember.findOne(
|
||||||
|
{ accountId, guildId: guild._id },
|
||||||
|
"RegularCreditsContributed MiscItemsContributed"
|
||||||
|
))!;
|
||||||
|
|
||||||
|
const techProject = guild.TechProjects!.find(x => x.ItemType == data.RecipeType)!;
|
||||||
|
|
||||||
|
if (data.VaultCredits) {
|
||||||
|
if (data.VaultCredits > techProject.ReqCredits) {
|
||||||
|
data.VaultCredits = techProject.ReqCredits;
|
||||||
|
}
|
||||||
|
techProject.ReqCredits -= data.VaultCredits;
|
||||||
|
guild.VaultRegularCredits! -= data.VaultCredits;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.RegularCredits > techProject.ReqCredits) {
|
||||||
|
data.RegularCredits = techProject.ReqCredits;
|
||||||
|
}
|
||||||
|
techProject.ReqCredits -= data.RegularCredits;
|
||||||
|
|
||||||
|
guildMember.RegularCreditsContributed ??= 0;
|
||||||
|
guildMember.RegularCreditsContributed += data.RegularCredits;
|
||||||
|
|
||||||
|
if (data.VaultMiscItems.length) {
|
||||||
|
for (const miscItem of data.VaultMiscItems) {
|
||||||
|
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
||||||
|
if (reqItem) {
|
||||||
|
if (miscItem.ItemCount > reqItem.ItemCount) {
|
||||||
|
miscItem.ItemCount = reqItem.ItemCount;
|
||||||
|
}
|
||||||
|
reqItem.ItemCount -= miscItem.ItemCount;
|
||||||
|
|
||||||
|
const vaultMiscItem = guild.VaultMiscItems!.find(x => x.ItemType == miscItem.ItemType)!;
|
||||||
|
vaultMiscItem.ItemCount -= miscItem.ItemCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const miscItemChanges = [];
|
||||||
|
for (const miscItem of data.MiscItems) {
|
||||||
|
const reqItem = techProject.ReqItems.find(x => x.ItemType == miscItem.ItemType);
|
||||||
|
if (reqItem) {
|
||||||
|
if (miscItem.ItemCount > reqItem.ItemCount) {
|
||||||
|
miscItem.ItemCount = reqItem.ItemCount;
|
||||||
|
}
|
||||||
|
reqItem.ItemCount -= miscItem.ItemCount;
|
||||||
|
miscItemChanges.push({
|
||||||
|
ItemType: miscItem.ItemType,
|
||||||
|
ItemCount: miscItem.ItemCount * -1
|
||||||
|
});
|
||||||
|
|
||||||
|
addGuildMemberMiscItemContribution(guildMember, miscItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addMiscItems(inventory, miscItemChanges);
|
||||||
|
const inventoryChanges: IInventoryChanges = updateCurrency(inventory, data.RegularCredits, false);
|
||||||
|
inventoryChanges.MiscItems = miscItemChanges;
|
||||||
|
|
||||||
|
// Check if research is fully funded now.
|
||||||
|
await processGuildTechProjectContributionsUpdate(guild, techProject);
|
||||||
|
|
||||||
|
await Promise.all([guild.save(), inventory.save(), guildMember.save()]);
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges,
|
||||||
|
Vault: getGuildVault(guild)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
addMiscItems(inventory, miscItemChanges);
|
|
||||||
const inventoryChanges: IInventoryChanges = updateCurrency(inventory, contributions.RegularCredits, false);
|
|
||||||
inventoryChanges.MiscItems = miscItemChanges;
|
|
||||||
|
|
||||||
// Check if research is fully funded now.
|
|
||||||
await processGuildTechProjectContributionsUpdate(guild, techProject);
|
|
||||||
|
|
||||||
await Promise.all([guild.save(), inventory.save(), guildMember.save()]);
|
|
||||||
res.json({
|
|
||||||
InventoryChanges: inventoryChanges,
|
|
||||||
Vault: getGuildVault(guild)
|
|
||||||
});
|
|
||||||
} else if (data.Action.split(",")[0] == "Buy") {
|
} else if (data.Action.split(",")[0] == "Buy") {
|
||||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
|
|
||||||
res.status(400).send("-1").end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const purchase = data as IGuildTechBuyRequest;
|
const purchase = data as IGuildTechBuyRequest;
|
||||||
const quantity = parseInt(data.Action.split(",")[1]);
|
if (purchase.Mode == "Guild") {
|
||||||
const recipeChanges = [
|
const guild = await getGuildForRequestEx(req, inventory);
|
||||||
{
|
if (
|
||||||
ItemType: purchase.RecipeType,
|
!hasAccessToDojo(inventory) ||
|
||||||
ItemCount: quantity
|
!(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))
|
||||||
|
) {
|
||||||
|
res.status(400).send("-1").end();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
];
|
const quantity = parseInt(data.Action.split(",")[1]);
|
||||||
addRecipes(inventory, recipeChanges);
|
const recipeChanges = [
|
||||||
const currencyChanges = updateCurrency(
|
{
|
||||||
inventory,
|
ItemType: purchase.RecipeType,
|
||||||
ExportDojoRecipes.research[purchase.RecipeType].replicatePrice,
|
ItemCount: quantity
|
||||||
false
|
}
|
||||||
);
|
];
|
||||||
await inventory.save();
|
addRecipes(inventory, recipeChanges);
|
||||||
// Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
|
const currencyChanges = updateCurrency(
|
||||||
res.json({
|
inventory,
|
||||||
inventoryChanges: {
|
ExportDojoRecipes.research[purchase.RecipeType].replicatePrice,
|
||||||
...currencyChanges,
|
false
|
||||||
Recipes: recipeChanges
|
);
|
||||||
}
|
await inventory.save();
|
||||||
});
|
// Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
|
||||||
|
res.json({
|
||||||
|
inventoryChanges: {
|
||||||
|
...currencyChanges,
|
||||||
|
Recipes: recipeChanges
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const inventoryChanges = claimSalvagedComponent(inventory, purchase.CategoryItemId!);
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
inventoryChanges: inventoryChanges
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if (data.Action == "Fabricate") {
|
} else if (data.Action == "Fabricate") {
|
||||||
|
const guild = await getGuildForRequestEx(req, inventory);
|
||||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
|
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Fabricator))) {
|
||||||
res.status(400).send("-1").end();
|
res.status(400).send("-1").end();
|
||||||
return;
|
return;
|
||||||
@ -206,6 +297,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
|||||||
// Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
|
// Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
|
||||||
res.json({ inventoryChanges: inventoryChanges });
|
res.json({ inventoryChanges: inventoryChanges });
|
||||||
} else if (data.Action == "Pause") {
|
} else if (data.Action == "Pause") {
|
||||||
|
const guild = await getGuildForRequestEx(req, inventory);
|
||||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
||||||
res.status(400).send("-1").end();
|
res.status(400).send("-1").end();
|
||||||
return;
|
return;
|
||||||
@ -217,6 +309,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
|||||||
await removePigmentsFromGuildMembers(guild._id);
|
await removePigmentsFromGuildMembers(guild._id);
|
||||||
res.end();
|
res.end();
|
||||||
} else if (data.Action == "Unpause") {
|
} else if (data.Action == "Unpause") {
|
||||||
|
const guild = await getGuildForRequestEx(req, inventory);
|
||||||
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
if (!hasAccessToDojo(inventory) || !(await hasGuildPermission(guild, accountId, GuildPermission.Tech))) {
|
||||||
res.status(400).send("-1").end();
|
res.status(400).send("-1").end();
|
||||||
return;
|
return;
|
||||||
@ -226,9 +319,66 @@ export const guildTechController: RequestHandler = async (req, res) => {
|
|||||||
guild.ActiveDojoColorResearch = data.RecipeType;
|
guild.ActiveDojoColorResearch = data.RecipeType;
|
||||||
await guild.save();
|
await guild.save();
|
||||||
res.end();
|
res.end();
|
||||||
|
} else if (data.Action == "Cancel" && data.CategoryItemId) {
|
||||||
|
const personalTechProjectIndex = inventory.PersonalTechProjects.findIndex(x =>
|
||||||
|
x.CategoryItemId?.equals(data.CategoryItemId)
|
||||||
|
);
|
||||||
|
const personalTechProject = inventory.PersonalTechProjects[personalTechProjectIndex];
|
||||||
|
inventory.PersonalTechProjects.splice(personalTechProjectIndex, 1);
|
||||||
|
|
||||||
|
const meta = ExportDojoRecipes.research[personalTechProject.ItemType];
|
||||||
|
const contributedCredits = meta.price - personalTechProject.ReqCredits;
|
||||||
|
const inventoryChanges = updateCurrency(inventory, contributedCredits * -1, false);
|
||||||
|
inventoryChanges.MiscItems = [];
|
||||||
|
for (const ingredient of meta.ingredients) {
|
||||||
|
const reqItem = personalTechProject.ReqItems.find(x => x.ItemType == ingredient.ItemType);
|
||||||
|
if (reqItem) {
|
||||||
|
const contributedItems = ingredient.ItemCount - reqItem.ItemCount;
|
||||||
|
inventoryChanges.MiscItems.push({
|
||||||
|
ItemType: ingredient.ItemType,
|
||||||
|
ItemCount: contributedItems
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
action: "Cancel",
|
||||||
|
isPersonal: true,
|
||||||
|
inventoryChanges: inventoryChanges,
|
||||||
|
personalTech: {
|
||||||
|
ItemId: toOid(personalTechProject._id)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (data.Action == "Rush" && data.CategoryItemId) {
|
||||||
|
const inventoryChanges: IInventoryChanges = {
|
||||||
|
...updateCurrency(inventory, 20, true),
|
||||||
|
...claimSalvagedComponent(inventory, data.CategoryItemId)
|
||||||
|
};
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
inventoryChanges: inventoryChanges
|
||||||
|
});
|
||||||
|
} else if (data.Action == "InstantFinish") {
|
||||||
|
if (data.TechProductCategory != "CrewShipWeapons" && data.TechProductCategory != "CrewShipWeaponSkins") {
|
||||||
|
throw new Error(`unexpected TechProductCategory: ${data.TechProductCategory}`);
|
||||||
|
}
|
||||||
|
const inventoryChanges = finishComponentRepair(inventory, data.TechProductCategory, data.CategoryItemId!);
|
||||||
|
inventoryChanges.MiscItems = [
|
||||||
|
{
|
||||||
|
ItemType: "/Lotus/Types/Items/MiscItems/InstantSalvageRepairItem",
|
||||||
|
ItemCount: -1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
addMiscItems(inventory, inventoryChanges.MiscItems);
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
inventoryChanges: inventoryChanges
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
||||||
throw new Error(`unknown guildTech action: ${data.Action}`);
|
throw new Error(`unhandled guildTech request`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -238,23 +388,70 @@ type TGuildTechRequest =
|
|||||||
| IGuildTechContributeRequest;
|
| IGuildTechContributeRequest;
|
||||||
|
|
||||||
interface IGuildTechBasicRequest {
|
interface IGuildTechBasicRequest {
|
||||||
Action: "Start" | "Fabricate" | "Pause" | "Unpause";
|
Action: "Start" | "Fabricate" | "Pause" | "Unpause" | "Cancel" | "Rush" | "InstantFinish";
|
||||||
Mode: "Guild";
|
Mode: "Guild" | "Personal";
|
||||||
RecipeType: string;
|
RecipeType: string;
|
||||||
|
TechProductCategory?: string;
|
||||||
|
CategoryItemId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IGuildTechBuyRequest {
|
interface IGuildTechBuyRequest extends Omit<IGuildTechBasicRequest, "Action"> {
|
||||||
Action: string;
|
Action: string;
|
||||||
Mode: "Guild";
|
|
||||||
RecipeType: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IGuildTechContributeRequest {
|
interface IGuildTechContributeRequest {
|
||||||
Action: "Contribute";
|
Action: "Contribute";
|
||||||
ResearchId: "";
|
ResearchId: string;
|
||||||
RecipeType: string;
|
RecipeType: string;
|
||||||
RegularCredits: number;
|
RegularCredits: number;
|
||||||
MiscItems: IMiscItem[];
|
MiscItems: IMiscItem[];
|
||||||
VaultCredits: number;
|
VaultCredits: number;
|
||||||
VaultMiscItems: IMiscItem[];
|
VaultMiscItems: IMiscItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSalvageCategory = (
|
||||||
|
category: "CrewShipWeapons" | "CrewShipWeaponSkins"
|
||||||
|
): "CrewShipSalvagedWeapons" | "CrewShipSalvagedWeaponSkins" => {
|
||||||
|
return category == "CrewShipWeapons" ? "CrewShipSalvagedWeapons" : "CrewShipSalvagedWeaponSkins";
|
||||||
|
};
|
||||||
|
|
||||||
|
const claimSalvagedComponent = (inventory: TInventoryDatabaseDocument, itemId: string): IInventoryChanges => {
|
||||||
|
// delete personal tech project
|
||||||
|
const personalTechProjectIndex = inventory.PersonalTechProjects.findIndex(x => x.CategoryItemId?.equals(itemId));
|
||||||
|
const personalTechProject = inventory.PersonalTechProjects[personalTechProjectIndex];
|
||||||
|
inventory.PersonalTechProjects.splice(personalTechProjectIndex, 1);
|
||||||
|
|
||||||
|
const category = personalTechProject.ProductCategory! as "CrewShipWeapons" | "CrewShipWeaponSkins";
|
||||||
|
return finishComponentRepair(inventory, category, itemId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const finishComponentRepair = (
|
||||||
|
inventory: TInventoryDatabaseDocument,
|
||||||
|
category: "CrewShipWeapons" | "CrewShipWeaponSkins",
|
||||||
|
itemId: string
|
||||||
|
): IInventoryChanges => {
|
||||||
|
const salvageCategory = getSalvageCategory(category);
|
||||||
|
|
||||||
|
// find salved part & delete it
|
||||||
|
const salvageIndex = inventory[salvageCategory].findIndex(x => x._id.equals(itemId));
|
||||||
|
const salvageItem = inventory[salvageCategory][salvageIndex];
|
||||||
|
inventory[salvageCategory].splice(salvageIndex, 1);
|
||||||
|
|
||||||
|
// add final item
|
||||||
|
const inventoryChanges = {
|
||||||
|
...(category == "CrewShipWeaponSkins"
|
||||||
|
? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint)
|
||||||
|
: addEquipment(inventory, category, salvageItem.ItemType, {
|
||||||
|
UpgradeFingerprint: salvageItem.UpgradeFingerprint
|
||||||
|
})),
|
||||||
|
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
inventoryChanges.RemovedIdItems = [
|
||||||
|
{
|
||||||
|
ItemId: { $oid: itemId }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return inventoryChanges;
|
||||||
|
};
|
||||||
|
@ -11,8 +11,10 @@ import {
|
|||||||
import { getAccountForRequest, getAccountFromSuffixedName, getSuffixedName } from "@/src/services/loginService";
|
import { getAccountForRequest, getAccountFromSuffixedName, getSuffixedName } from "@/src/services/loginService";
|
||||||
import { addItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
import { addItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
|
import { ExportFlavour } from "warframe-public-export-plus";
|
||||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||||
|
import { fromStoreItem, isStoreItem } from "@/src/services/itemDataService";
|
||||||
|
import { IOid } from "@/src/types/commonTypes";
|
||||||
|
|
||||||
export const inboxController: RequestHandler = async (req, res) => {
|
export const inboxController: RequestHandler = async (req, res) => {
|
||||||
const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
|
const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
|
||||||
@ -27,10 +29,10 @@ export const inboxController: RequestHandler = async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteMessageRead(deleteId as string);
|
await deleteMessageRead(parseOid(deleteId as string));
|
||||||
res.status(200).end();
|
res.status(200).end();
|
||||||
} else if (messageId) {
|
} else if (messageId) {
|
||||||
const message = await getMessage(messageId as string);
|
const message = await getMessage(parseOid(messageId as string));
|
||||||
message.r = true;
|
message.r = true;
|
||||||
await message.save();
|
await message.save();
|
||||||
|
|
||||||
@ -48,8 +50,8 @@ export const inboxController: RequestHandler = async (req, res) => {
|
|||||||
await addItems(
|
await addItems(
|
||||||
inventory,
|
inventory,
|
||||||
attachmentItems.map(attItem => ({
|
attachmentItems.map(attItem => ({
|
||||||
ItemType: attItem,
|
ItemType: isStoreItem(attItem) ? fromStoreItem(attItem) : attItem,
|
||||||
ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1
|
ItemCount: 1
|
||||||
})),
|
})),
|
||||||
inventoryChanges
|
inventoryChanges
|
||||||
);
|
);
|
||||||
@ -99,7 +101,7 @@ export const inboxController: RequestHandler = async (req, res) => {
|
|||||||
await createNewEventMessages(req);
|
await createNewEventMessages(req);
|
||||||
const messages = await Inbox.find({ ownerId: accountId }).sort({ date: 1 });
|
const messages = await Inbox.find({ ownerId: accountId }).sort({ date: 1 });
|
||||||
|
|
||||||
const latestClientMessage = messages.find(m => m._id.toString() === latestClientMessageId);
|
const latestClientMessage = messages.find(m => m._id.toString() === parseOid(latestClientMessageId as string));
|
||||||
|
|
||||||
if (!latestClientMessage) {
|
if (!latestClientMessage) {
|
||||||
logger.debug(`this should only happen after DeleteAllRead `);
|
logger.debug(`this should only happen after DeleteAllRead `);
|
||||||
@ -122,3 +124,11 @@ export const inboxController: RequestHandler = async (req, res) => {
|
|||||||
res.json({ Inbox: inbox });
|
res.json({ Inbox: inbox });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 33.6.0 has query arguments like lastMessage={"$oid":"68112baebf192e786d1502bb"} instead of lastMessage=68112baebf192e786d1502bb
|
||||||
|
const parseOid = (oid: string): string => {
|
||||||
|
if (oid[0] == "{") {
|
||||||
|
return (JSON.parse(oid) as IOid).$oid;
|
||||||
|
}
|
||||||
|
return oid;
|
||||||
|
};
|
||||||
|
@ -14,9 +14,16 @@ import {
|
|||||||
ExportVirtuals
|
ExportVirtuals
|
||||||
} from "warframe-public-export-plus";
|
} from "warframe-public-export-plus";
|
||||||
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
|
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
|
||||||
import { addMiscItems, allDailyAffiliationKeys, createLibraryDailyTask } from "@/src/services/inventoryService";
|
import {
|
||||||
|
addMiscItems,
|
||||||
|
allDailyAffiliationKeys,
|
||||||
|
cleanupInventory,
|
||||||
|
createLibraryDailyTask,
|
||||||
|
generateRewardSeed
|
||||||
|
} from "@/src/services/inventoryService";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||||
|
import { Types } from "mongoose";
|
||||||
|
|
||||||
export const inventoryController: RequestHandler = async (request, response) => {
|
export const inventoryController: RequestHandler = async (request, response) => {
|
||||||
const accountId = await getAccountIdForRequest(request);
|
const accountId = await getAccountIdForRequest(request);
|
||||||
@ -79,8 +86,10 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanupInventory(inventory);
|
||||||
|
|
||||||
inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000);
|
inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000);
|
||||||
await inventory.save();
|
//await inventory.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -89,9 +98,27 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
|||||||
new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown
|
new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown
|
||||||
) {
|
) {
|
||||||
handleSubsumeCompletion(inventory);
|
handleSubsumeCompletion(inventory);
|
||||||
await inventory.save();
|
//await inventory.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inventory.LastInventorySync) {
|
||||||
|
const lastSyncDuviriMood = Math.trunc(inventory.LastInventorySync.getTimestamp().getTime() / 7200000);
|
||||||
|
const currentDuviriMood = Math.trunc(Date.now() / 7200000);
|
||||||
|
if (lastSyncDuviriMood != currentDuviriMood) {
|
||||||
|
logger.debug(`refreshing duviri seed`);
|
||||||
|
if (!inventory.DuviriInfo) {
|
||||||
|
inventory.DuviriInfo = {
|
||||||
|
Seed: generateRewardSeed(),
|
||||||
|
NumCompletions: 0
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
inventory.DuviriInfo.Seed = generateRewardSeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inventory.LastInventorySync = new Types.ObjectId();
|
||||||
|
await inventory.save();
|
||||||
|
|
||||||
response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query));
|
response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -149,7 +176,7 @@ export const getInventoryResponse = async (
|
|||||||
inventoryResponse.ShipDecorations = [];
|
inventoryResponse.ShipDecorations = [];
|
||||||
for (const [uniqueName, item] of Object.entries(ExportResources)) {
|
for (const [uniqueName, item] of Object.entries(ExportResources)) {
|
||||||
if (item.productCategory == "ShipDecorations") {
|
if (item.productCategory == "ShipDecorations") {
|
||||||
inventoryResponse.ShipDecorations.push({ ItemType: uniqueName, ItemCount: 1 });
|
inventoryResponse.ShipDecorations.push({ ItemType: uniqueName, ItemCount: 999_999 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,7 +229,8 @@ export const getInventoryResponse = async (
|
|||||||
|
|
||||||
if (config.universalPolarityEverywhere) {
|
if (config.universalPolarityEverywhere) {
|
||||||
const Polarity: IPolarity[] = [];
|
const Polarity: IPolarity[] = [];
|
||||||
for (let i = 0; i != 12; ++i) {
|
// 12 is needed for necramechs. 15 is needed for plexus/crewshipharness.
|
||||||
|
for (let i = 0; i != 15; ++i) {
|
||||||
Polarity.push({
|
Polarity.push({
|
||||||
Slot: i,
|
Slot: i,
|
||||||
Value: ArtifactPolarity.Any
|
Value: ArtifactPolarity.Any
|
||||||
@ -257,12 +285,16 @@ export const getInventoryResponse = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.noDailyFocusLimit) {
|
||||||
|
inventoryResponse.DailyFocus = Math.max(999_999, 250000 + inventoryResponse.PlayerLevel * 5000);
|
||||||
|
}
|
||||||
|
|
||||||
if (inventoryResponse.InfestedFoundry) {
|
if (inventoryResponse.InfestedFoundry) {
|
||||||
applyCheatsToInfestedFoundry(inventoryResponse.InfestedFoundry);
|
applyCheatsToInfestedFoundry(inventoryResponse.InfestedFoundry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Omitting this field so opening the navigation resyncs the inventory which is more desirable for typical usage.
|
// Omitting this field so opening the navigation resyncs the inventory which is more desirable for typical usage.
|
||||||
//inventoryResponse.LastInventorySync = toOid(new Types.ObjectId());
|
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;
|
||||||
|
@ -21,7 +21,11 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
|
|
||||||
const myAddress = request.host.indexOf("warframe.com") == -1 ? request.host : config.myAddress;
|
const myAddress = request.host.indexOf("warframe.com") == -1 ? request.host : config.myAddress;
|
||||||
|
|
||||||
if (!account && config.autoCreateAccount && loginRequest.ClientType != "webui") {
|
if (
|
||||||
|
!account &&
|
||||||
|
((config.autoCreateAccount && loginRequest.ClientType != "webui") ||
|
||||||
|
loginRequest.ClientType == "webui-register")
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
|
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
|
||||||
let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja";
|
let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja";
|
||||||
@ -37,7 +41,7 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
password: loginRequest.password,
|
password: loginRequest.password,
|
||||||
DisplayName: name,
|
DisplayName: name,
|
||||||
CountryCode: loginRequest.lang.toUpperCase(),
|
CountryCode: loginRequest.lang.toUpperCase(),
|
||||||
ClientType: loginRequest.ClientType,
|
ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType,
|
||||||
CrossPlatformAllowed: true,
|
CrossPlatformAllowed: true,
|
||||||
ForceLogoutVersion: 0,
|
ForceLogoutVersion: 0,
|
||||||
ConsentNeeded: false,
|
ConsentNeeded: false,
|
||||||
@ -59,6 +63,11 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loginRequest.ClientType == "webui-register") {
|
||||||
|
response.status(400).json({ error: "account already exists" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isCorrectPassword(loginRequest.password, account.password)) {
|
if (!isCorrectPassword(loginRequest.password, account.password)) {
|
||||||
response.status(400).json({ error: "incorrect login data" });
|
response.status(400).json({ error: "incorrect login data" });
|
||||||
return;
|
return;
|
||||||
|
@ -3,9 +3,10 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
|||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
||||||
import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService";
|
import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService";
|
||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { generateRewardSeed, getInventory } from "@/src/services/inventoryService";
|
||||||
import { getInventoryResponse } from "./inventoryController";
|
import { getInventoryResponse } from "./inventoryController";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
|
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**** INPUT ****
|
**** INPUT ****
|
||||||
@ -53,9 +54,16 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
|||||||
logger.debug("mission report:", missionReport);
|
logger.debug("mission report:", missionReport);
|
||||||
|
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
|
const firstCompletion = missionReport.SortieId
|
||||||
|
? inventory.CompletedSorties.indexOf(missionReport.SortieId) == -1
|
||||||
|
: false;
|
||||||
const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport);
|
const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport);
|
||||||
|
|
||||||
if (missionReport.MissionStatus !== "GS_SUCCESS") {
|
if (
|
||||||
|
missionReport.MissionStatus !== "GS_SUCCESS" &&
|
||||||
|
!(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId)
|
||||||
|
) {
|
||||||
|
inventory.RewardSeed = generateRewardSeed();
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||||
res.json({
|
res.json({
|
||||||
@ -65,8 +73,16 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { MissionRewards, inventoryChanges, credits } = await addMissionRewards(inventory, missionReport);
|
const {
|
||||||
|
MissionRewards,
|
||||||
|
inventoryChanges,
|
||||||
|
credits,
|
||||||
|
AffiliationMods,
|
||||||
|
SyndicateXPItemReward,
|
||||||
|
ConquestCompletedMissionsCount
|
||||||
|
} = await addMissionRewards(inventory, missionReport, firstCompletion);
|
||||||
|
|
||||||
|
inventory.RewardSeed = generateRewardSeed();
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||||
|
|
||||||
@ -77,8 +93,11 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
|||||||
MissionRewards,
|
MissionRewards,
|
||||||
...credits,
|
...credits,
|
||||||
...inventoryUpdates,
|
...inventoryUpdates,
|
||||||
FusionPoints: inventoryChanges?.FusionPoints
|
//FusionPoints: inventoryChanges?.FusionPoints, // This in combination with InventoryJson or InventoryChanges seems to just double the number of endo shown, so unsure when this is needed.
|
||||||
});
|
SyndicateXPItemReward,
|
||||||
|
AffiliationMods,
|
||||||
|
ConquestCompletedMissionsCount
|
||||||
|
} satisfies IMissionInventoryUpdateResponse);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -17,12 +17,13 @@ import { getDefaultUpgrades } from "@/src/services/itemDataService";
|
|||||||
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
||||||
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
import { getRandomInt } from "@/src/services/rngService";
|
import { getRandomInt } from "@/src/services/rngService";
|
||||||
import { ExportSentinels } from "warframe-public-export-plus";
|
import { ExportSentinels, ExportWeapons, IDefaultUpgrade } from "warframe-public-export-plus";
|
||||||
import { Status } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { Status } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
|
||||||
interface IModularCraftRequest {
|
interface IModularCraftRequest {
|
||||||
WeaponType: string;
|
WeaponType: string;
|
||||||
Parts: string[];
|
Parts: string[];
|
||||||
|
isWebUi?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const modularWeaponCraftingController: RequestHandler = async (req, res) => {
|
export const modularWeaponCraftingController: RequestHandler = async (req, res) => {
|
||||||
@ -34,9 +35,9 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
|||||||
const category = modularWeaponTypes[data.WeaponType];
|
const category = modularWeaponTypes[data.WeaponType];
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
|
|
||||||
const defaultUpgrades = getDefaultUpgrades(data.Parts);
|
let defaultUpgrades: IDefaultUpgrade[] | undefined;
|
||||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
||||||
Configs: applyDefaultUpgrades(inventory, defaultUpgrades)
|
ModularParts: data.Parts
|
||||||
};
|
};
|
||||||
const inventoryChanges: IInventoryChanges = {};
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
if (category == "KubrowPets") {
|
if (category == "KubrowPets") {
|
||||||
@ -129,38 +130,63 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
|||||||
// Only save mutagen & antigen in the ModularParts.
|
// Only save mutagen & antigen in the ModularParts.
|
||||||
defaultOverwrites.ModularParts = [data.Parts[1], data.Parts[2]];
|
defaultOverwrites.ModularParts = [data.Parts[1], data.Parts[2]];
|
||||||
|
|
||||||
for (const specialItem of ExportSentinels[data.WeaponType].exalted!) {
|
const meta = ExportSentinels[data.WeaponType];
|
||||||
|
|
||||||
|
for (const specialItem of meta.exalted!) {
|
||||||
addSpecialItem(inventory, specialItem, inventoryChanges);
|
addSpecialItem(inventory, specialItem, inventoryChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultUpgrades = meta.defaultUpgrades;
|
||||||
|
} else {
|
||||||
|
defaultUpgrades = getDefaultUpgrades(data.Parts);
|
||||||
}
|
}
|
||||||
addEquipment(inventory, category, data.WeaponType, data.Parts, inventoryChanges, defaultOverwrites);
|
|
||||||
combineInventoryChanges(inventoryChanges, occupySlot(inventory, productCategoryToInventoryBin(category)!, false));
|
if (category == "MoaPets") {
|
||||||
|
const weapon = ExportSentinels[data.WeaponType].defaultWeapon;
|
||||||
|
if (weapon) {
|
||||||
|
const category = ExportWeapons[weapon].productCategory;
|
||||||
|
addEquipment(inventory, category, weapon, undefined, inventoryChanges);
|
||||||
|
combineInventoryChanges(
|
||||||
|
inventoryChanges,
|
||||||
|
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defaultOverwrites.Configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
||||||
|
addEquipment(inventory, category, data.WeaponType, defaultOverwrites, inventoryChanges);
|
||||||
|
combineInventoryChanges(
|
||||||
|
inventoryChanges,
|
||||||
|
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)
|
||||||
|
);
|
||||||
if (defaultUpgrades) {
|
if (defaultUpgrades) {
|
||||||
inventoryChanges.RawUpgrades = defaultUpgrades.map(x => ({ ItemType: x.ItemType, ItemCount: 1 }));
|
inventoryChanges.RawUpgrades = defaultUpgrades.map(x => ({ ItemType: x.ItemType, ItemCount: 1 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove credits & parts
|
// Remove credits & parts
|
||||||
const miscItemChanges = [];
|
const miscItemChanges = [];
|
||||||
for (const part of data.Parts) {
|
let currencyChanges = {};
|
||||||
miscItemChanges.push({
|
if (!data.isWebUi) {
|
||||||
ItemType: part,
|
for (const part of data.Parts) {
|
||||||
ItemCount: -1
|
miscItemChanges.push({
|
||||||
});
|
ItemType: part,
|
||||||
|
ItemCount: -1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
currencyChanges = updateCurrency(
|
||||||
|
inventory,
|
||||||
|
category == "Hoverboards" ||
|
||||||
|
category == "MoaPets" ||
|
||||||
|
category == "LongGuns" ||
|
||||||
|
category == "Pistols" ||
|
||||||
|
category == "KubrowPets"
|
||||||
|
? 5000
|
||||||
|
: 4000, // Definitely correct for Melee & OperatorAmps
|
||||||
|
false
|
||||||
|
);
|
||||||
|
addMiscItems(inventory, miscItemChanges);
|
||||||
}
|
}
|
||||||
const currencyChanges = updateCurrency(
|
|
||||||
inventory,
|
|
||||||
category == "Hoverboards" ||
|
|
||||||
category == "MoaPets" ||
|
|
||||||
category == "LongGuns" ||
|
|
||||||
category == "Pistols" ||
|
|
||||||
category == "KubrowPets"
|
|
||||||
? 5000
|
|
||||||
: 4000, // Definitely correct for Melee & OperatorAmps
|
|
||||||
false
|
|
||||||
);
|
|
||||||
addMiscItems(inventory, miscItemChanges);
|
|
||||||
await inventory.save();
|
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
// Tell client what we did
|
// Tell client what we did
|
||||||
res.json({
|
res.json({
|
||||||
InventoryChanges: {
|
InventoryChanges: {
|
||||||
|
@ -21,7 +21,11 @@ import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
|||||||
export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||||
const partTypeToParts: Record<string, string[]> = {};
|
const partTypeToParts: Record<string, string[]> = {};
|
||||||
for (const [uniqueName, data] of Object.entries(ExportWeapons)) {
|
for (const [uniqueName, data] of Object.entries(ExportWeapons)) {
|
||||||
if (data.partType && data.premiumPrice) {
|
if (
|
||||||
|
data.partType &&
|
||||||
|
data.premiumPrice &&
|
||||||
|
!data.excludeFromCodex // exclude pvp variants
|
||||||
|
) {
|
||||||
partTypeToParts[data.partType] ??= [];
|
partTypeToParts[data.partType] ??= [];
|
||||||
partTypeToParts[data.partType].push(uniqueName);
|
partTypeToParts[data.partType].push(uniqueName);
|
||||||
}
|
}
|
||||||
@ -41,24 +45,18 @@ export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
|||||||
const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts);
|
const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts);
|
||||||
const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
||||||
const inventoryChanges: IInventoryChanges = {
|
const inventoryChanges: IInventoryChanges = {
|
||||||
...addEquipment(
|
...addEquipment(inventory, category, weaponInfo.ItemType, {
|
||||||
inventory,
|
Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
|
||||||
category,
|
ItemName: payload.ItemName,
|
||||||
weaponInfo.ItemType,
|
Configs: configs,
|
||||||
weaponInfo.ModularParts,
|
ModularParts: weaponInfo.ModularParts,
|
||||||
{},
|
Polarity: [
|
||||||
{
|
{
|
||||||
Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
|
Slot: payload.PolarizeSlot,
|
||||||
ItemName: payload.ItemName,
|
Value: payload.PolarizeValue
|
||||||
Configs: configs,
|
}
|
||||||
Polarity: [
|
]
|
||||||
{
|
}),
|
||||||
Slot: payload.PolarizeSlot,
|
|
||||||
Value: payload.PolarizeValue
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
...occupySlot(inventory, productCategoryToInventoryBin(category)!, true),
|
...occupySlot(inventory, productCategoryToInventoryBin(category)!, true),
|
||||||
...updateCurrency(inventory, weaponInfo.PremiumPrice, true)
|
...updateCurrency(inventory, weaponInfo.PremiumPrice, true)
|
||||||
};
|
};
|
||||||
@ -143,7 +141,7 @@ const getModularWeaponSale = (
|
|||||||
getItemType: (parts: string[]) => string
|
getItemType: (parts: string[]) => string
|
||||||
): IModularWeaponSaleInfo => {
|
): IModularWeaponSaleInfo => {
|
||||||
const rng = new CRng(day);
|
const rng = new CRng(day);
|
||||||
const parts = partTypes.map(partType => rng.randomElement(partTypeToParts[partType]));
|
const parts = partTypes.map(partType => rng.randomElement(partTypeToParts[partType])!);
|
||||||
let partsCost = 0;
|
let partsCost = 0;
|
||||||
for (const part of parts) {
|
for (const part of parts) {
|
||||||
partsCost += ExportWeapons[part].premiumPrice!;
|
partsCost += ExportWeapons[part].premiumPrice!;
|
||||||
|
@ -1,10 +1,31 @@
|
|||||||
import { getInfNodes, getNemesisPasscode } from "@/src/helpers/nemesisHelpers";
|
import {
|
||||||
|
consumeModCharge,
|
||||||
|
encodeNemesisGuess,
|
||||||
|
getInfNodes,
|
||||||
|
getKnifeUpgrade,
|
||||||
|
getNemesisPasscode,
|
||||||
|
getNemesisPasscodeModTypes,
|
||||||
|
getWeaponsForManifest,
|
||||||
|
IKnifeResponse,
|
||||||
|
showdownNodes
|
||||||
|
} from "@/src/helpers/nemesisHelpers";
|
||||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
||||||
import { freeUpSlot, getInventory } from "@/src/services/inventoryService";
|
import { freeUpSlot, getInventory } from "@/src/services/inventoryService";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { SRng } from "@/src/services/rngService";
|
import { SRng } from "@/src/services/rngService";
|
||||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||||
import { IInnateDamageFingerprint, InventorySlot, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
|
import {
|
||||||
|
IInnateDamageFingerprint,
|
||||||
|
IInventoryClient,
|
||||||
|
INemesisClient,
|
||||||
|
InventorySlot,
|
||||||
|
IUpgradeClient,
|
||||||
|
IWeaponSkinClient,
|
||||||
|
LoadoutIndex,
|
||||||
|
TEquipmentKey
|
||||||
|
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
@ -49,7 +70,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
|||||||
} else if ((req.query.mode as string) == "p") {
|
} else if ((req.query.mode as string) == "p") {
|
||||||
const inventory = await getInventory(accountId, "Nemesis");
|
const inventory = await getInventory(accountId, "Nemesis");
|
||||||
const body = getJSONfromString<INemesisPrespawnCheckRequest>(String(req.body));
|
const body = getJSONfromString<INemesisPrespawnCheckRequest>(String(req.body));
|
||||||
const passcode = getNemesisPasscode(inventory.Nemesis!.fp, inventory.Nemesis!.Faction);
|
const passcode = getNemesisPasscode(inventory.Nemesis!);
|
||||||
let guessResult = 0;
|
let guessResult = 0;
|
||||||
if (inventory.Nemesis!.Faction == "FC_INFESTATION") {
|
if (inventory.Nemesis!.Faction == "FC_INFESTATION") {
|
||||||
for (let i = 0; i != 3; ++i) {
|
for (let i = 0; i != 3; ++i) {
|
||||||
@ -66,6 +87,83 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.json({ GuessResult: guessResult });
|
res.json({ GuessResult: guessResult });
|
||||||
|
} else if (req.query.mode == "r") {
|
||||||
|
const inventory = await getInventory(
|
||||||
|
accountId,
|
||||||
|
"Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
|
||||||
|
);
|
||||||
|
const body = getJSONfromString<INemesisRequiemRequest>(String(req.body));
|
||||||
|
if (inventory.Nemesis!.Faction == "FC_INFESTATION") {
|
||||||
|
const guess: number[] = [body.guess & 0xf, (body.guess >> 4) & 0xf, (body.guess >> 8) & 0xf];
|
||||||
|
const passcode = getNemesisPasscode(inventory.Nemesis!)[0];
|
||||||
|
|
||||||
|
// Add to GuessHistory
|
||||||
|
const result1 = passcode == guess[0] ? 0 : 1;
|
||||||
|
const result2 = passcode == guess[1] ? 0 : 1;
|
||||||
|
const result3 = passcode == guess[2] ? 0 : 1;
|
||||||
|
inventory.Nemesis!.GuessHistory.push(
|
||||||
|
encodeNemesisGuess(guess[0], result1, guess[1], result2, guess[2], result3)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Increase antivirus if correct antivirus mod is installed
|
||||||
|
const response: IKnifeResponse = {};
|
||||||
|
if (result1 == 0 || result2 == 0 || result3 == 0) {
|
||||||
|
let antivirusGain = 5;
|
||||||
|
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "DATAKNIFE"))!;
|
||||||
|
const dataknifeLoadout = loadout.DATAKNIFE.id(inventory.CurrentLoadOutIds[LoadoutIndex.DATAKNIFE].$oid);
|
||||||
|
const dataknifeConfigIndex = dataknifeLoadout?.s?.mod ?? 0;
|
||||||
|
const dataknifeUpgrades = inventory.DataKnives[0].Configs[dataknifeConfigIndex].Upgrades!;
|
||||||
|
for (const upgrade of body.knife!.AttachedUpgrades) {
|
||||||
|
switch (upgrade.ItemType) {
|
||||||
|
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndSpeedOnUseMod":
|
||||||
|
antivirusGain += 10;
|
||||||
|
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||||
|
break;
|
||||||
|
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusAndWeaponDamageOnUseMod":
|
||||||
|
antivirusGain += 10;
|
||||||
|
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||||
|
break;
|
||||||
|
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusLargeOnSingleUseMod": // Instant Secure
|
||||||
|
antivirusGain += 15;
|
||||||
|
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||||
|
break;
|
||||||
|
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusOnUseMod": // Immuno Shield
|
||||||
|
antivirusGain += 15;
|
||||||
|
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||||
|
break;
|
||||||
|
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusSmallOnSingleUseMod":
|
||||||
|
antivirusGain += 10;
|
||||||
|
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inventory.Nemesis!.HenchmenKilled += antivirusGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inventory.Nemesis!.HenchmenKilled >= 100) {
|
||||||
|
inventory.Nemesis!.HenchmenKilled = 100;
|
||||||
|
}
|
||||||
|
inventory.Nemesis!.InfNodes = getInfNodes("FC_INFESTATION", 0);
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.json(response);
|
||||||
|
} else {
|
||||||
|
const passcode = getNemesisPasscode(inventory.Nemesis!);
|
||||||
|
if (passcode[body.position] != body.guess) {
|
||||||
|
res.end();
|
||||||
|
} else {
|
||||||
|
inventory.Nemesis!.Rank += 1;
|
||||||
|
inventory.Nemesis!.InfNodes = getInfNodes(inventory.Nemesis!.Faction, inventory.Nemesis!.Rank);
|
||||||
|
await inventory.save();
|
||||||
|
res.json({ RankIncrease: 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ((req.query.mode as string) == "rs") {
|
||||||
|
// report spawn; POST but no application data in body
|
||||||
|
const inventory = await getInventory(accountId, "Nemesis");
|
||||||
|
inventory.Nemesis!.LastEnc = inventory.Nemesis!.MissionCount;
|
||||||
|
await inventory.save();
|
||||||
|
res.json({ LastEnc: inventory.Nemesis!.LastEnc });
|
||||||
} else if ((req.query.mode as string) == "s") {
|
} else if ((req.query.mode as string) == "s") {
|
||||||
const inventory = await getInventory(accountId, "Nemesis");
|
const inventory = await getInventory(accountId, "Nemesis");
|
||||||
const body = getJSONfromString<INemesisStartRequest>(String(req.body));
|
const body = getJSONfromString<INemesisStartRequest>(String(req.body));
|
||||||
@ -73,18 +171,7 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
|||||||
|
|
||||||
let weaponIdx = -1;
|
let weaponIdx = -1;
|
||||||
if (body.target.Faction != "FC_INFESTATION") {
|
if (body.target.Faction != "FC_INFESTATION") {
|
||||||
let weapons: readonly string[];
|
const weapons = getWeaponsForManifest(body.target.manifest);
|
||||||
if (body.target.manifest == "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix") {
|
|
||||||
weapons = kuvaLichVersionSixWeapons;
|
|
||||||
} else if (
|
|
||||||
body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour" ||
|
|
||||||
body.target.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree"
|
|
||||||
) {
|
|
||||||
weapons = corpusVersionThreeWeapons;
|
|
||||||
} else {
|
|
||||||
throw new Error(`unknown nemesis manifest: ${body.target.manifest}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1);
|
const initialWeaponIdx = new SRng(body.target.fp).randomInt(0, weapons.length - 1);
|
||||||
weaponIdx = initialWeaponIdx;
|
weaponIdx = initialWeaponIdx;
|
||||||
do {
|
do {
|
||||||
@ -126,6 +213,38 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
|||||||
res.json({
|
res.json({
|
||||||
target: inventory.toJSON().Nemesis
|
target: inventory.toJSON().Nemesis
|
||||||
});
|
});
|
||||||
|
} else if ((req.query.mode as string) == "w") {
|
||||||
|
const inventory = await getInventory(
|
||||||
|
accountId,
|
||||||
|
"Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
|
||||||
|
);
|
||||||
|
//const body = getJSONfromString<INemesisWeakenRequest>(String(req.body));
|
||||||
|
|
||||||
|
inventory.Nemesis!.InfNodes = [
|
||||||
|
{
|
||||||
|
Node: showdownNodes[inventory.Nemesis!.Faction],
|
||||||
|
Influence: 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
inventory.Nemesis!.Weakened = true;
|
||||||
|
|
||||||
|
const response: IKnifeResponse & { target: INemesisClient } = {
|
||||||
|
target: inventory.toJSON<IInventoryClient>().Nemesis!
|
||||||
|
};
|
||||||
|
|
||||||
|
// Consume charge of the correct requiem mod(s)
|
||||||
|
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "DATAKNIFE"))!;
|
||||||
|
const dataknifeLoadout = loadout.DATAKNIFE.id(inventory.CurrentLoadOutIds[LoadoutIndex.DATAKNIFE].$oid);
|
||||||
|
const dataknifeConfigIndex = dataknifeLoadout?.s?.mod ?? 0;
|
||||||
|
const dataknifeUpgrades = inventory.DataKnives[0].Configs[dataknifeConfigIndex].Upgrades!;
|
||||||
|
const modTypes = getNemesisPasscodeModTypes(inventory.Nemesis!);
|
||||||
|
for (const modType of modTypes) {
|
||||||
|
const upgrade = getKnifeUpgrade(inventory, dataknifeUpgrades, modType);
|
||||||
|
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
|
||||||
|
}
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.json(response);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
||||||
throw new Error(`unknown nemesis mode: ${String(req.query.mode)}`);
|
throw new Error(`unknown nemesis mode: ${String(req.query.mode)}`);
|
||||||
@ -173,38 +292,23 @@ interface INemesisPrespawnCheckRequest {
|
|||||||
potency?: number[];
|
potency?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const kuvaLichVersionSixWeapons = [
|
interface INemesisRequiemRequest {
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
|
guess: number; // grn/crp: 4 bits | coda: 3x 4 bits
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
|
position: number; // grn/crp: 0-2 | coda: 0
|
||||||
"/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
|
// knife field provided for coda only
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
|
knife?: IKnife;
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
|
}
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
|
|
||||||
"/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
|
|
||||||
"/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
|
|
||||||
"/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
|
|
||||||
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
|
|
||||||
];
|
|
||||||
|
|
||||||
const corpusVersionThreeWeapons = [
|
// interface INemesisWeakenRequest {
|
||||||
"/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
|
// target: INemesisClient;
|
||||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
|
// knife: IKnife;
|
||||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
|
// }
|
||||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
|
|
||||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
|
interface IKnife {
|
||||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
|
Item: IEquipmentClient;
|
||||||
"/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
|
Skins: IWeaponSkinClient[];
|
||||||
"/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
|
ModSlot: number;
|
||||||
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
|
CustSlot: number;
|
||||||
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
|
AttachedUpgrades: IUpgradeClient[];
|
||||||
];
|
HiddenWhenHolstered: boolean;
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ import { GuildPermission } from "@/src/types/guildTypes";
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { ExportDojoRecipes, ExportResources } from "warframe-public-export-plus";
|
import { ExportDojoRecipes, ExportResources } from "warframe-public-export-plus";
|
||||||
|
import { config } from "@/src/services/configService";
|
||||||
|
|
||||||
export const placeDecoInComponentController: RequestHandler = async (req, res) => {
|
export const placeDecoInComponentController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
@ -36,6 +37,7 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
|||||||
const deco = component.Decos.find(x => x._id.equals(request.MoveId))!;
|
const deco = component.Decos.find(x => x._id.equals(request.MoveId))!;
|
||||||
deco.Pos = request.Pos;
|
deco.Pos = request.Pos;
|
||||||
deco.Rot = request.Rot;
|
deco.Rot = request.Rot;
|
||||||
|
deco.Scale = request.Scale;
|
||||||
} else {
|
} else {
|
||||||
const deco =
|
const deco =
|
||||||
component.Decos[
|
component.Decos[
|
||||||
@ -44,6 +46,7 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
|||||||
Type: request.Type,
|
Type: request.Type,
|
||||||
Pos: request.Pos,
|
Pos: request.Pos,
|
||||||
Rot: request.Rot,
|
Rot: request.Rot,
|
||||||
|
Scale: request.Scale,
|
||||||
Name: request.Name,
|
Name: request.Name,
|
||||||
Sockets: request.Sockets
|
Sockets: request.Sockets
|
||||||
}) - 1
|
}) - 1
|
||||||
@ -62,42 +65,42 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
|||||||
guild.VaultShipDecorations!.find(x => x.ItemType == itemType)!.ItemCount -= 1;
|
guild.VaultShipDecorations!.find(x => x.ItemType == itemType)!.ItemCount -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!meta || (meta.price == 0 && meta.ingredients.length == 0)) {
|
if (deco.Type != "/Lotus/Objects/Tenno/Props/TnoPaintBotDojoDeco") {
|
||||||
deco.CompletionTime = new Date();
|
if (!meta || (meta.price == 0 && meta.ingredients.length == 0) || config.noDojoDecoBuildStage) {
|
||||||
} else if (
|
deco.CompletionTime = new Date();
|
||||||
guild.AutoContributeFromVault &&
|
if (meta) {
|
||||||
guild.VaultRegularCredits &&
|
|
||||||
guild.VaultMiscItems &&
|
|
||||||
deco.Type != "/Lotus/Objects/Tenno/Props/TnoPaintBotDojoDeco"
|
|
||||||
) {
|
|
||||||
if (guild.VaultRegularCredits >= scaleRequiredCount(guild.Tier, meta.price)) {
|
|
||||||
let enoughMiscItems = true;
|
|
||||||
for (const ingredient of meta.ingredients) {
|
|
||||||
if (
|
|
||||||
getVaultMiscItemCount(guild, ingredient.ItemType) <
|
|
||||||
scaleRequiredCount(guild.Tier, ingredient.ItemCount)
|
|
||||||
) {
|
|
||||||
enoughMiscItems = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (enoughMiscItems) {
|
|
||||||
guild.VaultRegularCredits -= scaleRequiredCount(guild.Tier, meta.price);
|
|
||||||
deco.RegularCredits = scaleRequiredCount(guild.Tier, meta.price);
|
|
||||||
|
|
||||||
deco.MiscItems = [];
|
|
||||||
for (const ingredient of meta.ingredients) {
|
|
||||||
guild.VaultMiscItems.find(x => x.ItemType == ingredient.ItemType)!.ItemCount -=
|
|
||||||
scaleRequiredCount(guild.Tier, ingredient.ItemCount);
|
|
||||||
deco.MiscItems.push({
|
|
||||||
ItemType: ingredient.ItemType,
|
|
||||||
ItemCount: scaleRequiredCount(guild.Tier, ingredient.ItemCount)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deco.CompletionTime = new Date(Date.now() + meta.time * 1000);
|
|
||||||
processDojoBuildMaterialsGathered(guild, meta);
|
processDojoBuildMaterialsGathered(guild, meta);
|
||||||
}
|
}
|
||||||
|
} else if (guild.AutoContributeFromVault && guild.VaultRegularCredits && guild.VaultMiscItems) {
|
||||||
|
if (guild.VaultRegularCredits >= scaleRequiredCount(guild.Tier, meta.price)) {
|
||||||
|
let enoughMiscItems = true;
|
||||||
|
for (const ingredient of meta.ingredients) {
|
||||||
|
if (
|
||||||
|
getVaultMiscItemCount(guild, ingredient.ItemType) <
|
||||||
|
scaleRequiredCount(guild.Tier, ingredient.ItemCount)
|
||||||
|
) {
|
||||||
|
enoughMiscItems = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enoughMiscItems) {
|
||||||
|
guild.VaultRegularCredits -= scaleRequiredCount(guild.Tier, meta.price);
|
||||||
|
deco.RegularCredits = scaleRequiredCount(guild.Tier, meta.price);
|
||||||
|
|
||||||
|
deco.MiscItems = [];
|
||||||
|
for (const ingredient of meta.ingredients) {
|
||||||
|
guild.VaultMiscItems.find(x => x.ItemType == ingredient.ItemType)!.ItemCount -=
|
||||||
|
scaleRequiredCount(guild.Tier, ingredient.ItemCount);
|
||||||
|
deco.MiscItems.push({
|
||||||
|
ItemType: ingredient.ItemType,
|
||||||
|
ItemCount: scaleRequiredCount(guild.Tier, ingredient.ItemCount)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deco.CompletionTime = new Date(Date.now() + meta.time * 1000);
|
||||||
|
processDojoBuildMaterialsGathered(guild, meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,9 +115,9 @@ interface IPlaceDecoInComponentRequest {
|
|||||||
Type: string;
|
Type: string;
|
||||||
Pos: number[];
|
Pos: number[];
|
||||||
Rot: number[];
|
Rot: number[];
|
||||||
|
Scale?: number;
|
||||||
Name?: string;
|
Name?: string;
|
||||||
Sockets?: number;
|
Sockets?: number;
|
||||||
Scale?: number; // only provided alongside MoveId and seems to always be 1
|
|
||||||
MoveId?: string;
|
MoveId?: string;
|
||||||
ShipDeco?: boolean;
|
ShipDeco?: boolean;
|
||||||
VaultDeco?: boolean;
|
VaultDeco?: boolean;
|
||||||
|
9
src/controllers/api/playedParkourTutorialController.ts
Normal file
9
src/controllers/api/playedParkourTutorialController.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const playedParkourTutorialController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
await Inventory.updateOne({ accountOwnerId: accountId }, { PlayedParkourTutorial: true });
|
||||||
|
res.end();
|
||||||
|
};
|
@ -8,7 +8,11 @@ export const releasePetController: RequestHandler = async (req, res) => {
|
|||||||
const inventory = await getInventory(accountId, "RegularCredits KubrowPets");
|
const inventory = await getInventory(accountId, "RegularCredits KubrowPets");
|
||||||
const payload = getJSONfromString<IReleasePetRequest>(String(req.body));
|
const payload = getJSONfromString<IReleasePetRequest>(String(req.body));
|
||||||
|
|
||||||
const inventoryChanges = updateCurrency(inventory, 25000, false);
|
const inventoryChanges = updateCurrency(
|
||||||
|
inventory,
|
||||||
|
payload.recipeName == "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe" ? 25000 : 0,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
inventoryChanges.RemovedIdItems = [{ ItemId: { $oid: payload.petId } }];
|
inventoryChanges.RemovedIdItems = [{ ItemId: { $oid: payload.petId } }];
|
||||||
inventory.KubrowPets.pull({ _id: payload.petId });
|
inventory.KubrowPets.pull({ _id: payload.petId });
|
||||||
@ -18,6 +22,6 @@ export const releasePetController: RequestHandler = async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface IReleasePetRequest {
|
interface IReleasePetRequest {
|
||||||
recipeName: "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe";
|
recipeName: "/Lotus/Types/Game/KubrowPet/ReleasePetRecipe" | "webui";
|
||||||
petId: string;
|
petId: string;
|
||||||
}
|
}
|
||||||
|
21
src/controllers/api/removeIgnoredUserController.ts
Normal file
21
src/controllers/api/removeIgnoredUserController.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { Account, Ignore } from "@/src/models/loginModel";
|
||||||
|
import { getAccountForRequest } from "@/src/services/loginService";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const removeIgnoredUserController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountForRequest(req);
|
||||||
|
const data = getJSONfromString<IRemoveIgnoredUserRequest>(String(req.body));
|
||||||
|
const ignoreeAccount = await Account.findOne(
|
||||||
|
{ DisplayName: data.playerName.substring(0, data.playerName.length - 1) },
|
||||||
|
"_id"
|
||||||
|
);
|
||||||
|
if (ignoreeAccount) {
|
||||||
|
await Ignore.deleteOne({ ignorer: accountId, ignoree: ignoreeAccount._id });
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IRemoveIgnoredUserRequest {
|
||||||
|
playerName: string;
|
||||||
|
}
|
@ -1,53 +1,41 @@
|
|||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
|
import { config } from "@/src/services/configService";
|
||||||
|
import { addEmailItem, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { ICompletedDialogue } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { ICompletedDialogue, IDialogueDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
export const saveDialogueController: RequestHandler = async (req, res) => {
|
export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const request = JSON.parse(String(req.body)) as SaveDialogueRequest;
|
const request = JSON.parse(String(req.body)) as SaveDialogueRequest;
|
||||||
if ("YearIteration" in request) {
|
if ("YearIteration" in request) {
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId, "DialogueHistory");
|
||||||
if (inventory.DialogueHistory) {
|
inventory.DialogueHistory ??= {};
|
||||||
inventory.DialogueHistory.YearIteration = request.YearIteration;
|
inventory.DialogueHistory.YearIteration = request.YearIteration;
|
||||||
} else {
|
|
||||||
inventory.DialogueHistory = { YearIteration: request.YearIteration };
|
|
||||||
}
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.end();
|
res.end();
|
||||||
} else {
|
} else {
|
||||||
const inventory = await getInventory(accountId);
|
const inventory = await getInventory(accountId);
|
||||||
if (!inventory.DialogueHistory) {
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
throw new Error("bad inventory state");
|
const tomorrowAt0Utc = config.noKimCooldowns
|
||||||
}
|
? Date.now()
|
||||||
if (request.QueuedDialogues.length != 0 || request.OtherDialogueInfos.length != 0) {
|
: (Math.trunc(Date.now() / 86400_000) + 1) * 86400_000;
|
||||||
logger.error(`saveDialogue request not fully handled: ${String(req.body)}`);
|
inventory.DialogueHistory ??= {};
|
||||||
}
|
|
||||||
inventory.DialogueHistory.Dialogues ??= [];
|
inventory.DialogueHistory.Dialogues ??= [];
|
||||||
let dialogue = inventory.DialogueHistory.Dialogues.find(x => x.DialogueName == request.DialogueName);
|
const dialogue = getDialogue(inventory, request.DialogueName);
|
||||||
if (!dialogue) {
|
|
||||||
dialogue =
|
|
||||||
inventory.DialogueHistory.Dialogues[
|
|
||||||
inventory.DialogueHistory.Dialogues.push({
|
|
||||||
Rank: 0,
|
|
||||||
Chemistry: 0,
|
|
||||||
AvailableDate: new Date(0),
|
|
||||||
AvailableGiftDate: new Date(0),
|
|
||||||
RankUpExpiry: new Date(0),
|
|
||||||
BountyChemExpiry: new Date(0),
|
|
||||||
Gifts: [],
|
|
||||||
Booleans: [],
|
|
||||||
Completed: [],
|
|
||||||
DialogueName: request.DialogueName
|
|
||||||
}) - 1
|
|
||||||
];
|
|
||||||
}
|
|
||||||
dialogue.Rank = request.Rank;
|
dialogue.Rank = request.Rank;
|
||||||
dialogue.Chemistry = request.Chemistry;
|
dialogue.Chemistry = request.Chemistry;
|
||||||
//dialogue.QueuedDialogues = request.QueuedDialogues;
|
dialogue.QueuedDialogues = request.QueuedDialogues;
|
||||||
for (const bool of request.Booleans) {
|
for (const bool of request.Booleans) {
|
||||||
dialogue.Booleans.push(bool);
|
dialogue.Booleans.push(bool);
|
||||||
|
if (bool == "LizzieShawzin") {
|
||||||
|
await addEmailItem(
|
||||||
|
inventory,
|
||||||
|
"/Lotus/Types/Items/EmailItems/LizzieShawzinSkinEmailItem",
|
||||||
|
inventoryChanges
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (const bool of request.ResetBooleans) {
|
for (const bool of request.ResetBooleans) {
|
||||||
const index = dialogue.Booleans.findIndex(x => x == bool);
|
const index = dialogue.Booleans.findIndex(x => x == bool);
|
||||||
@ -55,14 +43,38 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
|
|||||||
dialogue.Booleans.splice(index, 1);
|
dialogue.Booleans.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialogue.Completed.push(request.Data);
|
for (const info of request.OtherDialogueInfos) {
|
||||||
const tomorrowAt0Utc = (Math.trunc(Date.now() / (86400 * 1000)) + 1) * 86400 * 1000;
|
const otherDialogue = getDialogue(inventory, info.Dialogue);
|
||||||
dialogue.AvailableDate = new Date(tomorrowAt0Utc);
|
if (info.Tag != "") {
|
||||||
await inventory.save();
|
otherDialogue.QueuedDialogues.push(info.Tag);
|
||||||
res.json({
|
}
|
||||||
InventoryChanges: [],
|
otherDialogue.Chemistry += info.Value; // unsure
|
||||||
AvailableDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
|
}
|
||||||
});
|
if (request.Data) {
|
||||||
|
dialogue.Completed.push(request.Data);
|
||||||
|
dialogue.AvailableDate = new Date(tomorrowAt0Utc);
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges,
|
||||||
|
AvailableDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
|
||||||
|
});
|
||||||
|
} else if (request.Gift) {
|
||||||
|
const inventoryChanges = updateCurrency(inventory, request.Gift.Cost, false);
|
||||||
|
const gift = dialogue.Gifts.find(x => x.Item == request.Gift!.Item);
|
||||||
|
if (gift) {
|
||||||
|
gift.GiftedQuantity += 1;
|
||||||
|
} else {
|
||||||
|
dialogue.Gifts.push({ Item: request.Gift.Item, GiftedQuantity: 1 });
|
||||||
|
}
|
||||||
|
dialogue.AvailableGiftDate = new Date(tomorrowAt0Utc);
|
||||||
|
await inventory.save();
|
||||||
|
res.json({
|
||||||
|
InventoryChanges: inventoryChanges,
|
||||||
|
AvailableGiftDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,11 +89,17 @@ interface SaveCompletedDialogueRequest {
|
|||||||
Rank: number;
|
Rank: number;
|
||||||
Chemistry: number;
|
Chemistry: number;
|
||||||
CompletionType: number;
|
CompletionType: number;
|
||||||
QueuedDialogues: string[]; // unsure
|
QueuedDialogues: string[];
|
||||||
|
Gift?: {
|
||||||
|
Item: string;
|
||||||
|
GainedChemistry: number;
|
||||||
|
Cost: number;
|
||||||
|
GiftedQuantity: number;
|
||||||
|
};
|
||||||
Booleans: string[];
|
Booleans: string[];
|
||||||
ResetBooleans: string[];
|
ResetBooleans: string[];
|
||||||
Data: ICompletedDialogue;
|
Data?: ICompletedDialogue;
|
||||||
OtherDialogueInfos: IOtherDialogueInfo[]; // unsure
|
OtherDialogueInfos: IOtherDialogueInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IOtherDialogueInfo {
|
interface IOtherDialogueInfo {
|
||||||
@ -89,3 +107,26 @@ interface IOtherDialogueInfo {
|
|||||||
Tag: string;
|
Tag: string;
|
||||||
Value: number;
|
Value: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getDialogue = (inventory: TInventoryDatabaseDocument, dialogueName: string): IDialogueDatabase => {
|
||||||
|
let dialogue = inventory.DialogueHistory!.Dialogues!.find(x => x.DialogueName == dialogueName);
|
||||||
|
if (!dialogue) {
|
||||||
|
dialogue =
|
||||||
|
inventory.DialogueHistory!.Dialogues![
|
||||||
|
inventory.DialogueHistory!.Dialogues!.push({
|
||||||
|
Rank: 0,
|
||||||
|
Chemistry: 0,
|
||||||
|
AvailableDate: new Date(0),
|
||||||
|
AvailableGiftDate: new Date(0),
|
||||||
|
RankUpExpiry: new Date(0),
|
||||||
|
BountyChemExpiry: new Date(0),
|
||||||
|
QueuedDialogues: [],
|
||||||
|
Gifts: [],
|
||||||
|
Booleans: [],
|
||||||
|
Completed: [],
|
||||||
|
DialogueName: dialogueName
|
||||||
|
}) - 1
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return dialogue;
|
||||||
|
};
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
import { RequestHandler } from "express";
|
|
||||||
import { ISaveLoadoutRequest } from "@/src/types/saveLoadoutTypes";
|
|
||||||
import { handleInventoryItemConfigChange } from "@/src/services/saveLoadoutService";
|
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
|
||||||
import { logger } from "@/src/utils/logger";
|
|
||||||
|
|
||||||
export const saveLoadoutController: RequestHandler = async (req, res) => {
|
|
||||||
//validate here
|
|
||||||
const accountId = await getAccountIdForRequest(req);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const body: ISaveLoadoutRequest = JSON.parse(req.body as string) as ISaveLoadoutRequest;
|
|
||||||
// console.log(util.inspect(body, { showHidden: false, depth: null, colors: true }));
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const { UpgradeVer, ...equipmentChanges } = body;
|
|
||||||
const newLoadoutId = await handleInventoryItemConfigChange(equipmentChanges, accountId);
|
|
||||||
|
|
||||||
//send back new loadout id, if new loadout was added
|
|
||||||
if (newLoadoutId) {
|
|
||||||
res.send(newLoadoutId);
|
|
||||||
}
|
|
||||||
res.status(200).end();
|
|
||||||
} catch (error: unknown) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
logger.error(`error in saveLoadoutController: ${error.message}`);
|
|
||||||
res.status(400).json({ error: error.message });
|
|
||||||
} else {
|
|
||||||
res.status(400).json({ error: "unknown error" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
21
src/controllers/api/saveLoadoutController.ts
Normal file
21
src/controllers/api/saveLoadoutController.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { ISaveLoadoutRequest } from "@/src/types/saveLoadoutTypes";
|
||||||
|
import { handleInventoryItemConfigChange } from "@/src/services/saveLoadoutService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
|
||||||
|
export const saveLoadoutController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
|
||||||
|
const body: ISaveLoadoutRequest = JSON.parse(req.body as string) as ISaveLoadoutRequest;
|
||||||
|
// console.log(util.inspect(body, { showHidden: false, depth: null, colors: true }));
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { UpgradeVer, ...equipmentChanges } = body;
|
||||||
|
const newLoadoutId = await handleInventoryItemConfigChange(equipmentChanges, accountId);
|
||||||
|
|
||||||
|
//send back new loadout id, if new loadout was added
|
||||||
|
if (newLoadoutId) {
|
||||||
|
res.send(newLoadoutId);
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
};
|
@ -6,14 +6,20 @@ import {
|
|||||||
addRecipes,
|
addRecipes,
|
||||||
addMiscItems,
|
addMiscItems,
|
||||||
addConsumables,
|
addConsumables,
|
||||||
freeUpSlot
|
freeUpSlot,
|
||||||
|
combineInventoryChanges,
|
||||||
|
addCrewShipRawSalvage,
|
||||||
|
addFusionPoints
|
||||||
} from "@/src/services/inventoryService";
|
} from "@/src/services/inventoryService";
|
||||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
|
|
||||||
export const sellController: RequestHandler = async (req, res) => {
|
export const sellController: RequestHandler = async (req, res) => {
|
||||||
const payload = JSON.parse(String(req.body)) as ISellRequest;
|
const payload = JSON.parse(String(req.body)) as ISellRequest;
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const requiredFields = new Set();
|
const requiredFields = new Set<keyof TInventoryDatabaseDocument>();
|
||||||
if (payload.SellCurrency == "SC_RegularCredits") {
|
if (payload.SellCurrency == "SC_RegularCredits") {
|
||||||
requiredFields.add("RegularCredits");
|
requiredFields.add("RegularCredits");
|
||||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||||
@ -22,7 +28,7 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
requiredFields.add("MiscItems");
|
requiredFields.add("MiscItems");
|
||||||
}
|
}
|
||||||
for (const key of Object.keys(payload.Items)) {
|
for (const key of Object.keys(payload.Items)) {
|
||||||
requiredFields.add(key);
|
requiredFields.add(key as keyof TInventoryDatabaseDocument);
|
||||||
}
|
}
|
||||||
if (requiredFields.has("Upgrades")) {
|
if (requiredFields.has("Upgrades")) {
|
||||||
requiredFields.add("RawUpgrades");
|
requiredFields.add("RawUpgrades");
|
||||||
@ -39,7 +45,7 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) {
|
if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) {
|
||||||
requiredFields.add(InventorySlot.SPACEWEAPONS);
|
requiredFields.add(InventorySlot.SPACEWEAPONS);
|
||||||
}
|
}
|
||||||
if (payload.Items.Sentinels || payload.Items.SentinelWeapons) {
|
if (payload.Items.Sentinels || payload.Items.SentinelWeapons || payload.Items.MoaPets) {
|
||||||
requiredFields.add(InventorySlot.SENTINELS);
|
requiredFields.add(InventorySlot.SENTINELS);
|
||||||
}
|
}
|
||||||
if (payload.Items.OperatorAmps) {
|
if (payload.Items.OperatorAmps) {
|
||||||
@ -48,13 +54,23 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
if (payload.Items.Hoverboards) {
|
if (payload.Items.Hoverboards) {
|
||||||
requiredFields.add(InventorySlot.SPACESUITS);
|
requiredFields.add(InventorySlot.SPACESUITS);
|
||||||
}
|
}
|
||||||
|
if (payload.Items.CrewShipWeapons || payload.Items.CrewShipWeaponSkins) {
|
||||||
|
requiredFields.add(InventorySlot.RJ_COMPONENT_AND_ARMAMENTS);
|
||||||
|
requiredFields.add("CrewShipRawSalvage");
|
||||||
|
if (payload.Items.CrewShipWeapons) {
|
||||||
|
requiredFields.add("CrewShipSalvagedWeapons");
|
||||||
|
}
|
||||||
|
if (payload.Items.CrewShipWeaponSkins) {
|
||||||
|
requiredFields.add("CrewShipSalvagedWeaponSkins");
|
||||||
|
}
|
||||||
|
}
|
||||||
const inventory = await getInventory(accountId, Array.from(requiredFields).join(" "));
|
const inventory = await getInventory(accountId, Array.from(requiredFields).join(" "));
|
||||||
|
|
||||||
// Give currency
|
// Give currency
|
||||||
if (payload.SellCurrency == "SC_RegularCredits") {
|
if (payload.SellCurrency == "SC_RegularCredits") {
|
||||||
inventory.RegularCredits += payload.SellPrice;
|
inventory.RegularCredits += payload.SellPrice;
|
||||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||||
inventory.FusionPoints += payload.SellPrice;
|
addFusionPoints(inventory, payload.SellPrice);
|
||||||
} else if (payload.SellCurrency == "SC_PrimeBucks") {
|
} else if (payload.SellCurrency == "SC_PrimeBucks") {
|
||||||
addMiscItems(inventory, [
|
addMiscItems(inventory, [
|
||||||
{
|
{
|
||||||
@ -69,10 +85,14 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
ItemCount: payload.SellPrice
|
ItemCount: payload.SellPrice
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
} else if (payload.SellCurrency == "SC_Resources") {
|
||||||
|
// Will add appropriate MiscItems from CrewShipWeapons or CrewShipWeaponSkins
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unknown SellCurrency: " + payload.SellCurrency);
|
throw new Error("Unknown SellCurrency: " + payload.SellCurrency);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
|
|
||||||
// Remove item(s)
|
// Remove item(s)
|
||||||
if (payload.Items.Suits) {
|
if (payload.Items.Suits) {
|
||||||
payload.Items.Suits.forEach(sellItem => {
|
payload.Items.Suits.forEach(sellItem => {
|
||||||
@ -128,6 +148,12 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
freeUpSlot(inventory, InventorySlot.SENTINELS);
|
freeUpSlot(inventory, InventorySlot.SENTINELS);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (payload.Items.MoaPets) {
|
||||||
|
payload.Items.MoaPets.forEach(sellItem => {
|
||||||
|
inventory.MoaPets.pull({ _id: sellItem.String });
|
||||||
|
freeUpSlot(inventory, InventorySlot.SENTINELS);
|
||||||
|
});
|
||||||
|
}
|
||||||
if (payload.Items.OperatorAmps) {
|
if (payload.Items.OperatorAmps) {
|
||||||
payload.Items.OperatorAmps.forEach(sellItem => {
|
payload.Items.OperatorAmps.forEach(sellItem => {
|
||||||
inventory.OperatorAmps.pull({ _id: sellItem.String });
|
inventory.OperatorAmps.pull({ _id: sellItem.String });
|
||||||
@ -145,6 +171,56 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
inventory.Drones.pull({ _id: sellItem.String });
|
inventory.Drones.pull({ _id: sellItem.String });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (payload.Items.CrewShipWeapons) {
|
||||||
|
payload.Items.CrewShipWeapons.forEach(sellItem => {
|
||||||
|
if (sellItem.String[0] == "/") {
|
||||||
|
addCrewShipRawSalvage(inventory, [
|
||||||
|
{
|
||||||
|
ItemType: sellItem.String,
|
||||||
|
ItemCount: sellItem.Count * -1
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
const index = inventory.CrewShipWeapons.findIndex(x => x._id.equals(sellItem.String));
|
||||||
|
if (index != -1) {
|
||||||
|
if (payload.SellCurrency == "SC_Resources") {
|
||||||
|
refundPartialBuildCosts(inventory, inventory.CrewShipWeapons[index].ItemType, inventoryChanges);
|
||||||
|
}
|
||||||
|
inventory.CrewShipWeapons.splice(index, 1);
|
||||||
|
freeUpSlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS);
|
||||||
|
} else {
|
||||||
|
inventory.CrewShipSalvagedWeapons.pull({ _id: sellItem.String });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (payload.Items.CrewShipWeaponSkins) {
|
||||||
|
payload.Items.CrewShipWeaponSkins.forEach(sellItem => {
|
||||||
|
if (sellItem.String[0] == "/") {
|
||||||
|
addCrewShipRawSalvage(inventory, [
|
||||||
|
{
|
||||||
|
ItemType: sellItem.String,
|
||||||
|
ItemCount: sellItem.Count * -1
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
const index = inventory.CrewShipWeaponSkins.findIndex(x => x._id.equals(sellItem.String));
|
||||||
|
if (index != -1) {
|
||||||
|
if (payload.SellCurrency == "SC_Resources") {
|
||||||
|
refundPartialBuildCosts(
|
||||||
|
inventory,
|
||||||
|
inventory.CrewShipWeaponSkins[index].ItemType,
|
||||||
|
inventoryChanges
|
||||||
|
);
|
||||||
|
}
|
||||||
|
inventory.CrewShipWeaponSkins.splice(index, 1);
|
||||||
|
freeUpSlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS);
|
||||||
|
} else {
|
||||||
|
inventory.CrewShipSalvagedWeaponSkins.pull({ _id: sellItem.String });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
if (payload.Items.Consumables) {
|
if (payload.Items.Consumables) {
|
||||||
const consumablesChanges = [];
|
const consumablesChanges = [];
|
||||||
for (const sellItem of payload.Items.Consumables) {
|
for (const sellItem of payload.Items.Consumables) {
|
||||||
@ -191,7 +267,9 @@ export const sellController: RequestHandler = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.json({});
|
res.json({
|
||||||
|
inventoryChanges: inventoryChanges // "inventoryChanges" for this response instead of the usual "InventoryChanges"
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ISellRequest {
|
interface ISellRequest {
|
||||||
@ -209,9 +287,12 @@ interface ISellRequest {
|
|||||||
SpaceMelee?: ISellItem[];
|
SpaceMelee?: ISellItem[];
|
||||||
Sentinels?: ISellItem[];
|
Sentinels?: ISellItem[];
|
||||||
SentinelWeapons?: ISellItem[];
|
SentinelWeapons?: ISellItem[];
|
||||||
|
MoaPets?: ISellItem[];
|
||||||
OperatorAmps?: ISellItem[];
|
OperatorAmps?: ISellItem[];
|
||||||
Hoverboards?: ISellItem[];
|
Hoverboards?: ISellItem[];
|
||||||
Drones?: ISellItem[];
|
Drones?: ISellItem[];
|
||||||
|
CrewShipWeapons?: ISellItem[];
|
||||||
|
CrewShipWeaponSkins?: ISellItem[];
|
||||||
};
|
};
|
||||||
SellPrice: number;
|
SellPrice: number;
|
||||||
SellCurrency:
|
SellCurrency:
|
||||||
@ -228,3 +309,33 @@ interface ISellItem {
|
|||||||
String: string; // oid or uniqueName
|
String: string; // oid or uniqueName
|
||||||
Count: number;
|
Count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const refundPartialBuildCosts = (
|
||||||
|
inventory: TInventoryDatabaseDocument,
|
||||||
|
itemType: string,
|
||||||
|
inventoryChanges: IInventoryChanges
|
||||||
|
): void => {
|
||||||
|
// House versions
|
||||||
|
const research = Object.values(ExportDojoRecipes.research).find(x => x.resultType == itemType);
|
||||||
|
if (research) {
|
||||||
|
const miscItemChanges = research.ingredients.map(x => ({
|
||||||
|
ItemType: x.ItemType,
|
||||||
|
ItemCount: Math.trunc(x.ItemCount * 0.8)
|
||||||
|
}));
|
||||||
|
addMiscItems(inventory, miscItemChanges);
|
||||||
|
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sigma versions
|
||||||
|
const recipe = Object.values(ExportDojoRecipes.fabrications).find(x => x.resultType == itemType);
|
||||||
|
if (recipe) {
|
||||||
|
const miscItemChanges = recipe.ingredients.map(x => ({
|
||||||
|
ItemType: x.ItemType,
|
||||||
|
ItemCount: Math.trunc(x.ItemCount * 0.8)
|
||||||
|
}));
|
||||||
|
addMiscItems(inventory, miscItemChanges);
|
||||||
|
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
31
src/controllers/api/sendMsgToInBoxController.ts
Normal file
31
src/controllers/api/sendMsgToInBoxController.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { createMessage } from "@/src/services/inboxService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const sendMsgToInBoxController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const data = getJSONfromString<ISendMsgToInBoxRequest>(String(req.body));
|
||||||
|
await createMessage(accountId, [
|
||||||
|
{
|
||||||
|
sub: data.title,
|
||||||
|
msg: data.message,
|
||||||
|
sndr: data.sender ?? "/Lotus/Language/Bosses/Ordis",
|
||||||
|
icon: data.senderIcon,
|
||||||
|
highPriority: data.highPriority,
|
||||||
|
transmission: data.transmission,
|
||||||
|
att: data.attachments
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ISendMsgToInBoxRequest {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
sender?: string;
|
||||||
|
senderIcon?: string;
|
||||||
|
highPriority?: boolean;
|
||||||
|
transmission?: string;
|
||||||
|
attachments?: string[];
|
||||||
|
}
|
21
src/controllers/api/setHubNpcCustomizationsController.ts
Normal file
21
src/controllers/api/setHubNpcCustomizationsController.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { IHubNpcCustomization } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const setHubNpcCustomizationsController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId, "HubNpcCustomizations");
|
||||||
|
const upload = getJSONfromString<IHubNpcCustomization>(String(req.body));
|
||||||
|
inventory.HubNpcCustomizations ??= [];
|
||||||
|
const cust = inventory.HubNpcCustomizations.find(x => x.Tag == upload.Tag);
|
||||||
|
if (cust) {
|
||||||
|
cust.Colors = upload.Colors;
|
||||||
|
cust.Pattern = upload.Pattern;
|
||||||
|
} else {
|
||||||
|
inventory.HubNpcCustomizations.push(upload);
|
||||||
|
}
|
||||||
|
await inventory.save();
|
||||||
|
res.end();
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { ISetPlacedDecoInfoRequest } from "@/src/types/shipTypes";
|
import { IPictureFrameInfo, ISetPlacedDecoInfoRequest } from "@/src/types/shipTypes";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { handleSetPlacedDecoInfo } from "@/src/services/shipCustomizationsService";
|
import { handleSetPlacedDecoInfo } from "@/src/services/shipCustomizationsService";
|
||||||
|
|
||||||
@ -7,5 +7,17 @@ export const setPlacedDecoInfoController: RequestHandler = async (req, res) => {
|
|||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const payload = JSON.parse(req.body as string) as ISetPlacedDecoInfoRequest;
|
const payload = JSON.parse(req.body as string) as ISetPlacedDecoInfoRequest;
|
||||||
await handleSetPlacedDecoInfo(accountId, payload);
|
await handleSetPlacedDecoInfo(accountId, payload);
|
||||||
res.end();
|
res.json({
|
||||||
|
DecoId: payload.DecoId,
|
||||||
|
IsPicture: true,
|
||||||
|
PictureFrameInfo: payload.PictureFrameInfo,
|
||||||
|
BootLocation: payload.BootLocation
|
||||||
|
} satisfies ISetPlacedDecoInfoResponse);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface ISetPlacedDecoInfoResponse {
|
||||||
|
DecoId: string;
|
||||||
|
IsPicture: boolean;
|
||||||
|
PictureFrameInfo?: IPictureFrameInfo;
|
||||||
|
BootLocation?: string;
|
||||||
|
}
|
||||||
|
@ -3,29 +3,40 @@ import { RequestHandler } from "express";
|
|||||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||||
import { IOid } from "@/src/types/commonTypes";
|
import { IOid } from "@/src/types/commonTypes";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
|
import { IFavouriteLoadoutDatabase, TBootLocation } from "@/src/types/shipTypes";
|
||||||
|
|
||||||
export const setShipFavouriteLoadoutController: RequestHandler = async (req, res) => {
|
export const setShipFavouriteLoadoutController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const personalRooms = await getPersonalRooms(accountId);
|
const personalRooms = await getPersonalRooms(accountId);
|
||||||
const body = JSON.parse(String(req.body)) as ISetShipFavouriteLoadoutRequest;
|
const body = JSON.parse(String(req.body)) as ISetShipFavouriteLoadoutRequest;
|
||||||
if (body.BootLocation != "SHOP") {
|
if (body.BootLocation == "LISET") {
|
||||||
|
personalRooms.Ship.FavouriteLoadoutId = new Types.ObjectId(body.FavouriteLoadoutId.$oid);
|
||||||
|
} else if (body.BootLocation == "APARTMENT") {
|
||||||
|
updateTaggedDisplay(personalRooms.Apartment.FavouriteLoadouts, body);
|
||||||
|
} else if (body.BootLocation == "SHOP") {
|
||||||
|
updateTaggedDisplay(personalRooms.TailorShop.FavouriteLoadouts, body);
|
||||||
|
} else {
|
||||||
|
console.log(body);
|
||||||
throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
|
throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
|
||||||
}
|
}
|
||||||
const display = personalRooms.TailorShop.FavouriteLoadouts.find(x => x.Tag == body.TagName);
|
|
||||||
if (display) {
|
|
||||||
display.LoadoutId = new Types.ObjectId(body.FavouriteLoadoutId.$oid);
|
|
||||||
} else {
|
|
||||||
personalRooms.TailorShop.FavouriteLoadouts.push({
|
|
||||||
Tag: body.TagName,
|
|
||||||
LoadoutId: new Types.ObjectId(body.FavouriteLoadoutId.$oid)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await personalRooms.save();
|
await personalRooms.save();
|
||||||
res.json({});
|
res.json(body);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ISetShipFavouriteLoadoutRequest {
|
interface ISetShipFavouriteLoadoutRequest {
|
||||||
BootLocation: string;
|
BootLocation: TBootLocation;
|
||||||
FavouriteLoadoutId: IOid;
|
FavouriteLoadoutId: IOid;
|
||||||
TagName: string;
|
TagName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateTaggedDisplay = (arr: IFavouriteLoadoutDatabase[], body: ISetShipFavouriteLoadoutRequest): void => {
|
||||||
|
const display = arr.find(x => x.Tag == body.TagName!);
|
||||||
|
if (display) {
|
||||||
|
display.LoadoutId = new Types.ObjectId(body.FavouriteLoadoutId.$oid);
|
||||||
|
} else {
|
||||||
|
arr.push({
|
||||||
|
Tag: body.TagName!,
|
||||||
|
LoadoutId: new Types.ObjectId(body.FavouriteLoadoutId.$oid)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
48
src/controllers/api/setShipVignetteController.ts
Normal file
48
src/controllers/api/setShipVignetteController.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { addMiscItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||||
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
|
import { logger } from "@/src/utils/logger";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const setShipVignetteController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId, "MiscItems");
|
||||||
|
const personalRooms = await getPersonalRooms(accountId);
|
||||||
|
const body = JSON.parse(String(req.body)) as ISetShipVignetteRequest;
|
||||||
|
personalRooms.Ship.Wallpaper = body.Wallpaper;
|
||||||
|
personalRooms.Ship.Vignette = body.Vignette;
|
||||||
|
personalRooms.Ship.VignetteFish ??= [];
|
||||||
|
const inventoryChanges: IInventoryChanges = {};
|
||||||
|
for (let i = 0; i != body.Fish.length; ++i) {
|
||||||
|
if (body.Fish[i] && !personalRooms.Ship.VignetteFish[i]) {
|
||||||
|
logger.debug(`moving ${body.Fish[i]} from inventory to vignette slot ${i}`);
|
||||||
|
const miscItemsDelta = [{ ItemType: body.Fish[i], ItemCount: -1 }];
|
||||||
|
addMiscItems(inventory, miscItemsDelta);
|
||||||
|
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemsDelta });
|
||||||
|
} else if (personalRooms.Ship.VignetteFish[i] && !body.Fish[i]) {
|
||||||
|
logger.debug(`moving ${personalRooms.Ship.VignetteFish[i]} from vignette slot ${i} to inventory`);
|
||||||
|
const miscItemsDelta = [{ ItemType: personalRooms.Ship.VignetteFish[i], ItemCount: +1 }];
|
||||||
|
addMiscItems(inventory, miscItemsDelta);
|
||||||
|
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemsDelta });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
personalRooms.Ship.VignetteFish = body.Fish;
|
||||||
|
if (body.VignetteDecos.length) {
|
||||||
|
logger.error(`setShipVignette request not fully handled:`, body);
|
||||||
|
}
|
||||||
|
await Promise.all([inventory.save(), personalRooms.save()]);
|
||||||
|
res.json({
|
||||||
|
Wallpaper: body.Wallpaper,
|
||||||
|
Vignette: body.Vignette,
|
||||||
|
VignetteFish: body.Fish,
|
||||||
|
InventoryChanges: inventoryChanges
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ISetShipVignetteRequest {
|
||||||
|
Wallpaper: string;
|
||||||
|
Vignette: string;
|
||||||
|
Fish: string[];
|
||||||
|
VignetteDecos: unknown[];
|
||||||
|
}
|
@ -3,15 +3,12 @@ import { RequestHandler } from "express";
|
|||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { ExportNightwave, ExportSyndicates, ISyndicateSacrifice } from "warframe-public-export-plus";
|
import { ExportNightwave, ExportSyndicates, ISyndicateSacrifice } from "warframe-public-export-plus";
|
||||||
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||||
import {
|
import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||||
addItem,
|
|
||||||
addMiscItems,
|
|
||||||
combineInventoryChanges,
|
|
||||||
getInventory,
|
|
||||||
updateCurrency
|
|
||||||
} from "@/src/services/inventoryService";
|
|
||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
import { fromStoreItem, isStoreItem } from "@/src/services/itemDataService";
|
import { isStoreItem, toStoreItem } from "@/src/services/itemDataService";
|
||||||
|
import { logger } from "@/src/utils/logger";
|
||||||
|
|
||||||
|
const nightwaveCredsItemType = ExportNightwave.rewards[ExportNightwave.rewards.length - 1].uniqueName;
|
||||||
|
|
||||||
export const syndicateSacrificeController: RequestHandler = async (request, response) => {
|
export const syndicateSacrificeController: RequestHandler = async (request, response) => {
|
||||||
const accountId = await getAccountIdForRequest(request);
|
const accountId = await getAccountIdForRequest(request);
|
||||||
@ -57,7 +54,7 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
|
|||||||
syndicate.Title ??= 0;
|
syndicate.Title ??= 0;
|
||||||
syndicate.Title += 1;
|
syndicate.Title += 1;
|
||||||
|
|
||||||
if (syndicate.Title > 0 && manifest.favours.length != 0) {
|
if (syndicate.Title > 0 && manifest.favours.find(x => x.rankUpReward && x.requiredLevel == syndicate.Title)) {
|
||||||
syndicate.FreeFavorsEarned ??= [];
|
syndicate.FreeFavorsEarned ??= [];
|
||||||
if (!syndicate.FreeFavorsEarned.includes(syndicate.Title)) {
|
if (!syndicate.FreeFavorsEarned.includes(syndicate.Title)) {
|
||||||
syndicate.FreeFavorsEarned.push(syndicate.Title);
|
syndicate.FreeFavorsEarned.push(syndicate.Title);
|
||||||
@ -77,10 +74,17 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
|
|||||||
res.NewEpisodeReward = true;
|
res.NewEpisodeReward = true;
|
||||||
const reward = ExportNightwave.rewards[index];
|
const reward = ExportNightwave.rewards[index];
|
||||||
let rewardType = reward.uniqueName;
|
let rewardType = reward.uniqueName;
|
||||||
if (isStoreItem(rewardType)) {
|
if (!isStoreItem(rewardType)) {
|
||||||
rewardType = fromStoreItem(rewardType);
|
rewardType = toStoreItem(rewardType);
|
||||||
}
|
}
|
||||||
combineInventoryChanges(res.InventoryChanges, await addItem(inventory, rewardType, reward.itemCount ?? 1));
|
const rewardInventoryChanges = (await handleStoreItemAcquisition(rewardType, inventory, reward.itemCount))
|
||||||
|
.InventoryChanges;
|
||||||
|
if (Object.keys(rewardInventoryChanges).length == 0) {
|
||||||
|
logger.debug(`nightwave rank up reward did not seem to get added, giving 50 creds instead`);
|
||||||
|
rewardInventoryChanges.MiscItems = [{ ItemType: nightwaveCredsItemType, ItemCount: 50 }];
|
||||||
|
addMiscItems(inventory, rewardInventoryChanges.MiscItems);
|
||||||
|
}
|
||||||
|
combineInventoryChanges(res.InventoryChanges, rewardInventoryChanges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,9 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import {
|
import { addMiscItems, addStanding, freeUpSlot, getInventory } from "@/src/services/inventoryService";
|
||||||
addMiscItems,
|
|
||||||
freeUpSlot,
|
|
||||||
getInventory,
|
|
||||||
getStandingLimit,
|
|
||||||
updateStandingLimit
|
|
||||||
} from "@/src/services/inventoryService";
|
|
||||||
import { IMiscItem, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { IMiscItem, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { IOid } from "@/src/types/commonTypes";
|
import { IOid } from "@/src/types/commonTypes";
|
||||||
import { ExportSyndicates, ExportWeapons } from "warframe-public-export-plus";
|
import { ExportSyndicates, ExportWeapons } from "warframe-public-export-plus";
|
||||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||||
import { EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
import { EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
@ -61,38 +54,13 @@ export const syndicateStandingBonusController: RequestHandler = async (req, res)
|
|||||||
inventoryChanges[slotBin] = { count: -1, platinum: 0, Slots: 1 };
|
inventoryChanges[slotBin] = { count: -1, platinum: 0, Slots: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
let syndicate = inventory.Affiliations.find(x => x.Tag == request.Operation.AffiliationTag);
|
const affiliationMod = addStanding(inventory, request.Operation.AffiliationTag, gainedStanding, true);
|
||||||
if (!syndicate) {
|
|
||||||
syndicate =
|
|
||||||
inventory.Affiliations[
|
|
||||||
inventory.Affiliations.push({ Tag: request.Operation.AffiliationTag, Standing: 0 }) - 1
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0);
|
|
||||||
if (syndicate.Standing + gainedStanding > max) {
|
|
||||||
gainedStanding = max - syndicate.Standing;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syndicateMeta.medallionsCappedByDailyLimit) {
|
|
||||||
if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) {
|
|
||||||
gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin);
|
|
||||||
}
|
|
||||||
updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding);
|
|
||||||
}
|
|
||||||
|
|
||||||
syndicate.Standing += gainedStanding;
|
|
||||||
|
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
InventoryChanges: inventoryChanges,
|
InventoryChanges: inventoryChanges,
|
||||||
AffiliationMods: [
|
AffiliationMods: [affiliationMod]
|
||||||
{
|
|
||||||
Tag: request.Operation.AffiliationTag,
|
|
||||||
Standing: gainedStanding
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,7 +25,13 @@ export const upgradesController: RequestHandler = async (req, res) => {
|
|||||||
operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker"
|
operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/CustomizationSlotUnlocker"
|
||||||
) {
|
) {
|
||||||
updateCurrency(inventory, 10, true);
|
updateCurrency(inventory, 10, true);
|
||||||
} else if (operation.OperationType != "UOT_ABILITY_OVERRIDE") {
|
} else if (
|
||||||
|
operation.OperationType != "UOT_SWAP_POLARITY" &&
|
||||||
|
operation.OperationType != "UOT_ABILITY_OVERRIDE"
|
||||||
|
) {
|
||||||
|
if (!operation.UpgradeRequirement) {
|
||||||
|
throw new Error(`${operation.OperationType} operation should be free?`);
|
||||||
|
}
|
||||||
addMiscItems(inventory, [
|
addMiscItems(inventory, [
|
||||||
{
|
{
|
||||||
ItemType: operation.UpgradeRequirement,
|
ItemType: operation.UpgradeRequirement,
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||||
|
|
||||||
export const addCurrencyController: RequestHandler = async (req, res) => {
|
export const addCurrencyController: RequestHandler = async (req, res) => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
const inventory = await getInventory(accountId);
|
|
||||||
const request = req.body as IAddCurrencyRequest;
|
const request = req.body as IAddCurrencyRequest;
|
||||||
inventory[request.currency] += request.delta;
|
const inventory = await getInventory(accountId, request.currency);
|
||||||
|
if (request.currency == "FusionPoints") {
|
||||||
|
addFusionPoints(inventory, request.delta);
|
||||||
|
} else {
|
||||||
|
inventory[request.currency] += request.delta;
|
||||||
|
}
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.end();
|
res.end();
|
||||||
};
|
};
|
||||||
|
44
src/controllers/custom/addMissingMaxRankModsController.ts
Normal file
44
src/controllers/custom/addMissingMaxRankModsController.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
import { ExportArcanes, ExportUpgrades } from "warframe-public-export-plus";
|
||||||
|
|
||||||
|
export const addMissingMaxRankModsController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId, "Upgrades");
|
||||||
|
|
||||||
|
const maxOwnedRanks: Record<string, number> = {};
|
||||||
|
for (const upgrade of inventory.Upgrades) {
|
||||||
|
const fingerprint = JSON.parse(upgrade.UpgradeFingerprint ?? "{}") as { lvl?: number };
|
||||||
|
if (fingerprint.lvl) {
|
||||||
|
maxOwnedRanks[upgrade.ItemType] ??= 0;
|
||||||
|
if (fingerprint.lvl > maxOwnedRanks[upgrade.ItemType]) {
|
||||||
|
maxOwnedRanks[upgrade.ItemType] = fingerprint.lvl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [uniqueName, data] of Object.entries(ExportUpgrades)) {
|
||||||
|
if (data.fusionLimit != 0 && data.type != "PARAZON" && maxOwnedRanks[uniqueName] != data.fusionLimit) {
|
||||||
|
inventory.Upgrades.push({
|
||||||
|
ItemType: uniqueName,
|
||||||
|
UpgradeFingerprint: JSON.stringify({ lvl: data.fusionLimit })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [uniqueName, data] of Object.entries(ExportArcanes)) {
|
||||||
|
if (
|
||||||
|
data.name != "/Lotus/Language/Items/GenericCosmeticEnhancerName" &&
|
||||||
|
maxOwnedRanks[uniqueName] != data.fusionLimit
|
||||||
|
) {
|
||||||
|
inventory.Upgrades.push({
|
||||||
|
ItemType: uniqueName,
|
||||||
|
UpgradeFingerprint: JSON.stringify({ lvl: data.fusionLimit })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.end();
|
||||||
|
};
|
@ -1,98 +0,0 @@
|
|||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
|
||||||
import {
|
|
||||||
getInventory,
|
|
||||||
addEquipment,
|
|
||||||
occupySlot,
|
|
||||||
productCategoryToInventoryBin,
|
|
||||||
applyDefaultUpgrades
|
|
||||||
} from "@/src/services/inventoryService";
|
|
||||||
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
|
||||||
import { getDefaultUpgrades } from "@/src/services/itemDataService";
|
|
||||||
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
|
||||||
import { ExportWeapons } from "warframe-public-export-plus";
|
|
||||||
import { RequestHandler } from "express";
|
|
||||||
|
|
||||||
export const addModularEquipmentController: RequestHandler = async (req, res) => {
|
|
||||||
const requiredFields = new Set();
|
|
||||||
const accountId = await getAccountIdForRequest(req);
|
|
||||||
const request = req.body as IAddModularEquipmentRequest;
|
|
||||||
const category = modularWeaponTypes[request.ItemType];
|
|
||||||
const inventoryBin = productCategoryToInventoryBin(category)!;
|
|
||||||
requiredFields.add(category);
|
|
||||||
requiredFields.add(inventoryBin);
|
|
||||||
|
|
||||||
request.ModularParts.forEach(part => {
|
|
||||||
if (ExportWeapons[part].gunType) {
|
|
||||||
if (category == "LongGuns") {
|
|
||||||
request.ItemType = {
|
|
||||||
GT_RIFLE: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimary",
|
|
||||||
GT_SHOTGUN: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryShotgun",
|
|
||||||
GT_BEAM: "/Lotus/Weapons/SolarisUnited/Primary/LotusModularPrimaryBeam"
|
|
||||||
}[ExportWeapons[part].gunType];
|
|
||||||
} else {
|
|
||||||
request.ItemType = {
|
|
||||||
GT_RIFLE: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondary",
|
|
||||||
GT_SHOTGUN: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryShotgun",
|
|
||||||
GT_BEAM: "/Lotus/Weapons/SolarisUnited/Secondary/LotusModularSecondaryBeam"
|
|
||||||
}[ExportWeapons[part].gunType];
|
|
||||||
}
|
|
||||||
} else if (request.ItemType == "/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetPowerSuit") {
|
|
||||||
if (part.includes("ZanukaPetPartHead")) {
|
|
||||||
request.ItemType = {
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA":
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit",
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB":
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit",
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC":
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit"
|
|
||||||
}[part]!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const defaultUpgrades = getDefaultUpgrades(request.ModularParts);
|
|
||||||
if (defaultUpgrades) {
|
|
||||||
requiredFields.add("RawUpgrades");
|
|
||||||
}
|
|
||||||
const defaultWeaponsMap: Record<string, string[]> = {
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetAPowerSuit": [
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponIP"
|
|
||||||
],
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetBPowerSuit": [
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponIS"
|
|
||||||
],
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetCPowerSuit": [
|
|
||||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetMeleeWeaponPS"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const defaultWeapons = defaultWeaponsMap[request.ItemType] as string[] | undefined;
|
|
||||||
if (defaultWeapons) {
|
|
||||||
for (const defaultWeapon of defaultWeapons) {
|
|
||||||
const category = ExportWeapons[defaultWeapon].productCategory;
|
|
||||||
requiredFields.add(category);
|
|
||||||
requiredFields.add(productCategoryToInventoryBin(category));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const inventory = await getInventory(accountId, Array.from(requiredFields).join(" "));
|
|
||||||
if (defaultWeapons) {
|
|
||||||
for (const defaultWeapon of defaultWeapons) {
|
|
||||||
const category = ExportWeapons[defaultWeapon].productCategory;
|
|
||||||
addEquipment(inventory, category, defaultWeapon);
|
|
||||||
occupySlot(inventory, productCategoryToInventoryBin(category)!, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
|
||||||
Configs: applyDefaultUpgrades(inventory, defaultUpgrades)
|
|
||||||
};
|
|
||||||
|
|
||||||
addEquipment(inventory, category, request.ItemType, request.ModularParts, undefined, defaultOverwrites);
|
|
||||||
occupySlot(inventory, inventoryBin, true);
|
|
||||||
await inventory.save();
|
|
||||||
res.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IAddModularEquipmentRequest {
|
|
||||||
ItemType: string;
|
|
||||||
ModularParts: string[];
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import { addGearExpByCategory, getInventory } from "@/src/services/inventoryService";
|
import { applyClientEquipmentUpdates, getInventory } from "@/src/services/inventoryService";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
@ -20,7 +20,7 @@ export const addXpController: RequestHandler = async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addGearExpByCategory(inventory, gear, category as TEquipmentKey);
|
applyClientEquipmentUpdates(inventory, gear, category as TEquipmentKey);
|
||||||
}
|
}
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
res.end();
|
res.end();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { Account } from "@/src/models/loginModel";
|
import { Account, Ignore } from "@/src/models/loginModel";
|
||||||
import { Inbox } from "@/src/models/inboxModel";
|
import { Inbox } from "@/src/models/inboxModel";
|
||||||
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
import { Inventory } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
||||||
@ -23,6 +23,8 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
Account.deleteOne({ _id: accountId }),
|
Account.deleteOne({ _id: accountId }),
|
||||||
GuildMember.deleteMany({ accountId: accountId }),
|
GuildMember.deleteMany({ accountId: accountId }),
|
||||||
|
Ignore.deleteMany({ ignorer: accountId }),
|
||||||
|
Ignore.deleteMany({ ignoree: accountId }),
|
||||||
Inbox.deleteMany({ ownerId: accountId }),
|
Inbox.deleteMany({ ownerId: accountId }),
|
||||||
Inventory.deleteOne({ accountOwnerId: accountId }),
|
Inventory.deleteOne({ accountOwnerId: accountId }),
|
||||||
Leaderboard.deleteMany({ ownerId: accountId }),
|
Leaderboard.deleteMany({ ownerId: accountId }),
|
||||||
|
@ -3,6 +3,7 @@ import { getDict, getItemName, getString } from "@/src/services/itemDataService"
|
|||||||
import {
|
import {
|
||||||
ExportArcanes,
|
ExportArcanes,
|
||||||
ExportAvionics,
|
ExportAvionics,
|
||||||
|
ExportCustoms,
|
||||||
ExportDrones,
|
ExportDrones,
|
||||||
ExportGear,
|
ExportGear,
|
||||||
ExportKeys,
|
ExportKeys,
|
||||||
@ -19,6 +20,7 @@ import {
|
|||||||
TRelicQuality
|
TRelicQuality
|
||||||
} from "warframe-public-export-plus";
|
} from "warframe-public-export-plus";
|
||||||
import archonCrystalUpgrades from "@/static/fixed_responses/webuiArchonCrystalUpgrades.json";
|
import archonCrystalUpgrades from "@/static/fixed_responses/webuiArchonCrystalUpgrades.json";
|
||||||
|
import allIncarnons from "@/static/fixed_responses/allIncarnonList.json";
|
||||||
|
|
||||||
interface ListedItem {
|
interface ListedItem {
|
||||||
uniqueName: string;
|
uniqueName: string;
|
||||||
@ -28,6 +30,30 @@ interface ListedItem {
|
|||||||
badReason?: "starter" | "frivolous" | "notraw";
|
badReason?: "starter" | "frivolous" | "notraw";
|
||||||
partType?: string;
|
partType?: string;
|
||||||
chainLength?: number;
|
chainLength?: number;
|
||||||
|
parazon?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ItemLists {
|
||||||
|
archonCrystalUpgrades: Record<string, string>;
|
||||||
|
uniqueLevelCaps: Record<string, number>;
|
||||||
|
Suits: ListedItem[];
|
||||||
|
LongGuns: ListedItem[];
|
||||||
|
Melee: ListedItem[];
|
||||||
|
ModularParts: ListedItem[];
|
||||||
|
Pistols: ListedItem[];
|
||||||
|
Sentinels: ListedItem[];
|
||||||
|
SentinelWeapons: ListedItem[];
|
||||||
|
SpaceGuns: ListedItem[];
|
||||||
|
SpaceMelee: ListedItem[];
|
||||||
|
SpaceSuits: ListedItem[];
|
||||||
|
MechSuits: ListedItem[];
|
||||||
|
miscitems: ListedItem[];
|
||||||
|
Syndicates: ListedItem[];
|
||||||
|
OperatorAmps: ListedItem[];
|
||||||
|
QuestKeys: ListedItem[];
|
||||||
|
KubrowPets: ListedItem[];
|
||||||
|
EvolutionProgress: ListedItem[];
|
||||||
|
mods: ListedItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const relicQualitySuffixes: Record<TRelicQuality, string> = {
|
const relicQualitySuffixes: Record<TRelicQuality, string> = {
|
||||||
@ -39,22 +65,28 @@ const relicQualitySuffixes: Record<TRelicQuality, string> = {
|
|||||||
|
|
||||||
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: Record<string, ListedItem[]> = {};
|
const res: ItemLists = {
|
||||||
res.Suits = [];
|
archonCrystalUpgrades,
|
||||||
res.LongGuns = [];
|
uniqueLevelCaps: ExportMisc.uniqueLevelCaps,
|
||||||
res.Melee = [];
|
Suits: [],
|
||||||
res.ModularParts = [];
|
LongGuns: [],
|
||||||
res.Pistols = [];
|
Melee: [],
|
||||||
res.Sentinels = [];
|
ModularParts: [],
|
||||||
res.SentinelWeapons = [];
|
Pistols: [],
|
||||||
res.SpaceGuns = [];
|
Sentinels: [],
|
||||||
res.SpaceMelee = [];
|
SentinelWeapons: [],
|
||||||
res.SpaceSuits = [];
|
SpaceGuns: [],
|
||||||
res.MechSuits = [];
|
SpaceMelee: [],
|
||||||
res.miscitems = [];
|
SpaceSuits: [],
|
||||||
res.Syndicates = [];
|
MechSuits: [],
|
||||||
res.OperatorAmps = [];
|
miscitems: [],
|
||||||
res.QuestKeys = [];
|
Syndicates: [],
|
||||||
|
OperatorAmps: [],
|
||||||
|
QuestKeys: [],
|
||||||
|
KubrowPets: [],
|
||||||
|
EvolutionProgress: [],
|
||||||
|
mods: []
|
||||||
|
};
|
||||||
for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
|
for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
|
||||||
res[item.productCategory].push({
|
res[item.productCategory].push({
|
||||||
uniqueName,
|
uniqueName,
|
||||||
@ -63,20 +95,23 @@ const getItemListsController: RequestHandler = (req, response) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
|
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
|
||||||
if (item.productCategory == "Sentinels") {
|
if (item.productCategory == "Sentinels" || item.productCategory == "KubrowPets") {
|
||||||
res[item.productCategory].push({
|
res[item.productCategory].push({
|
||||||
uniqueName,
|
uniqueName,
|
||||||
name: getString(item.name, lang)
|
name: getString(item.name, lang),
|
||||||
|
exalted: item.exalted
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
|
for (const [uniqueName, item] of Object.entries(ExportWeapons)) {
|
||||||
if (item.partType) {
|
if (item.partType) {
|
||||||
res.ModularParts.push({
|
if (!uniqueName.startsWith("/Lotus/Types/Items/Deimos/")) {
|
||||||
uniqueName,
|
res.ModularParts.push({
|
||||||
name: getString(item.name, lang),
|
uniqueName,
|
||||||
partType: item.partType
|
name: getString(item.name, lang),
|
||||||
});
|
partType: item.partType
|
||||||
|
});
|
||||||
|
}
|
||||||
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
|
if (uniqueName.split("/")[5] != "SentTrainingAmplifier") {
|
||||||
res.miscitems.push({
|
res.miscitems.push({
|
||||||
uniqueName: uniqueName,
|
uniqueName: uniqueName,
|
||||||
@ -109,12 +144,28 @@ const getItemListsController: RequestHandler = (req, response) => {
|
|||||||
let name = getString(item.name, lang);
|
let name = getString(item.name, lang);
|
||||||
if ("dissectionParts" in item) {
|
if ("dissectionParts" in item) {
|
||||||
name = getString("/Lotus/Language/Fish/FishDisplayName", lang).split("|FISH_NAME|").join(name);
|
name = getString("/Lotus/Language/Fish/FishDisplayName", lang).split("|FISH_NAME|").join(name);
|
||||||
if (uniqueName.indexOf("Large") != -1) {
|
if (item.syndicateTag == "CetusSyndicate") {
|
||||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeLargeAbbrev", lang));
|
if (uniqueName.indexOf("Large") != -1) {
|
||||||
} else if (uniqueName.indexOf("Medium") != -1) {
|
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeLargeAbbrev", lang));
|
||||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeMediumAbbrev", lang));
|
} else if (uniqueName.indexOf("Medium") != -1) {
|
||||||
|
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeMediumAbbrev", lang));
|
||||||
|
} else {
|
||||||
|
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeSmallAbbrev", lang));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeSmallAbbrev", lang));
|
if (uniqueName.indexOf("Large") != -1) {
|
||||||
|
name = name
|
||||||
|
.split("|FISH_SIZE|")
|
||||||
|
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryElderAbbrev", lang));
|
||||||
|
} else if (uniqueName.indexOf("Medium") != -1) {
|
||||||
|
name = name
|
||||||
|
.split("|FISH_SIZE|")
|
||||||
|
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryMatureAbbrev", lang));
|
||||||
|
} else {
|
||||||
|
name = name
|
||||||
|
.split("|FISH_SIZE|")
|
||||||
|
.join(getString("/Lotus/Language/SolarisVenus/RobofishAgeCategoryYoungAbbrev", lang));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@ -171,8 +222,13 @@ const getItemListsController: RequestHandler = (req, response) => {
|
|||||||
name: getString(item.name, lang)
|
name: getString(item.name, lang)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
for (const [uniqueName, item] of Object.entries(ExportCustoms)) {
|
||||||
|
res.miscitems.push({
|
||||||
|
uniqueName: uniqueName,
|
||||||
|
name: getString(item.name, lang)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
res.mods = [];
|
|
||||||
for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) {
|
for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) {
|
||||||
const mod: ListedItem = {
|
const mod: ListedItem = {
|
||||||
uniqueName,
|
uniqueName,
|
||||||
@ -186,6 +242,9 @@ const getItemListsController: RequestHandler = (req, response) => {
|
|||||||
} else if (upgrade.upgradeEntries) {
|
} else if (upgrade.upgradeEntries) {
|
||||||
mod.badReason = "notraw";
|
mod.badReason = "notraw";
|
||||||
}
|
}
|
||||||
|
if (upgrade.type == "PARAZON") {
|
||||||
|
mod.parazon = true;
|
||||||
|
}
|
||||||
res.mods.push(mod);
|
res.mods.push(mod);
|
||||||
}
|
}
|
||||||
for (const [uniqueName, upgrade] of Object.entries(ExportAvionics)) {
|
for (const [uniqueName, upgrade] of Object.entries(ExportAvionics)) {
|
||||||
@ -227,12 +286,14 @@ const getItemListsController: RequestHandler = (req, response) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const uniqueName of allIncarnons) {
|
||||||
|
res.EvolutionProgress.push({
|
||||||
|
uniqueName,
|
||||||
|
name: getString(getItemName(uniqueName) || "", lang)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
response.json({
|
response.json(res);
|
||||||
archonCrystalUpgrades,
|
|
||||||
uniqueLevelCaps: ExportMisc.uniqueLevelCaps,
|
|
||||||
...res
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getItemListsController };
|
export { getItemListsController };
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
import { getInventory } from "@/src/services/inventoryService";
|
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
|
||||||
import { EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
|
||||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
|
||||||
import { RequestHandler } from "express";
|
|
||||||
|
|
||||||
export const gildEquipmentController: RequestHandler = async (req, res) => {
|
|
||||||
const accountId = await getAccountIdForRequest(req);
|
|
||||||
const request = req.body as IGildEquipmentRequest;
|
|
||||||
const inventory = await getInventory(accountId, request.Category);
|
|
||||||
const weapon = inventory[request.Category].id(request.ItemId);
|
|
||||||
if (weapon) {
|
|
||||||
weapon.Features ??= 0;
|
|
||||||
weapon.Features |= EquipmentFeatures.GILDED;
|
|
||||||
await inventory.save();
|
|
||||||
}
|
|
||||||
res.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
type IGildEquipmentRequest = {
|
|
||||||
ItemId: string;
|
|
||||||
Category: TEquipmentKey;
|
|
||||||
};
|
|
@ -35,10 +35,8 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
|
|||||||
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case "completeAll": {
|
case "completeAll": {
|
||||||
if (allQuestKeys.includes(questItemType)) {
|
for (const questKey of inventory.QuestKeys) {
|
||||||
for (const questKey of inventory.QuestKeys) {
|
await completeQuest(inventory, questKey.ItemType);
|
||||||
await completeQuest(inventory, questKey.ItemType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -56,15 +54,12 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "deleteKey": {
|
case "deleteKey": {
|
||||||
if (allQuestKeys.includes(questItemType)) {
|
const questKey = inventory.QuestKeys.find(key => key.ItemType === questItemType);
|
||||||
const questKey = inventory.QuestKeys.find(key => key.ItemType === questItemType);
|
if (!questKey) {
|
||||||
if (!questKey) {
|
logger.error(`Quest key not found in inventory: ${questItemType}`);
|
||||||
logger.error(`Quest key not found in inventory: ${questItemType}`);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
inventory.QuestKeys.pull({ ItemType: questItemType });
|
|
||||||
}
|
}
|
||||||
|
inventory.QuestKeys.pull({ ItemType: questItemType });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "completeKey": {
|
case "completeKey": {
|
||||||
|
33
src/controllers/custom/setEvolutionProgressController.ts
Normal file
33
src/controllers/custom/setEvolutionProgressController.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { getInventory } from "@/src/services/inventoryService";
|
||||||
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
|
export const setEvolutionProgressController: RequestHandler = async (req, res) => {
|
||||||
|
const accountId = await getAccountIdForRequest(req);
|
||||||
|
const inventory = await getInventory(accountId);
|
||||||
|
const payload = req.body as ISetEvolutionProgressRequest;
|
||||||
|
|
||||||
|
inventory.EvolutionProgress ??= [];
|
||||||
|
payload.forEach(element => {
|
||||||
|
const entry = inventory.EvolutionProgress!.find(entry => entry.ItemType === element.ItemType);
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
entry.Progress = 0;
|
||||||
|
entry.Rank = element.Rank;
|
||||||
|
} else {
|
||||||
|
inventory.EvolutionProgress!.push({
|
||||||
|
Progress: 0,
|
||||||
|
Rank: element.Rank,
|
||||||
|
ItemType: element.ItemType
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await inventory.save();
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
type ISetEvolutionProgressRequest = {
|
||||||
|
ItemType: string;
|
||||||
|
Rank: number;
|
||||||
|
}[];
|
@ -1,611 +1,6 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
|
import { getWorldState } from "@/src/services/worldStateService";
|
||||||
import static1999FallDays from "@/static/fixed_responses/worldState/1999_fall_days.json";
|
|
||||||
import static1999SpringDays from "@/static/fixed_responses/worldState/1999_spring_days.json";
|
|
||||||
import static1999SummerDays from "@/static/fixed_responses/worldState/1999_summer_days.json";
|
|
||||||
import static1999WinterDays from "@/static/fixed_responses/worldState/1999_winter_days.json";
|
|
||||||
import { buildConfig } from "@/src/services/buildConfigService";
|
|
||||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
|
||||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
|
||||||
import { config } from "@/src/services/configService";
|
|
||||||
import { CRng } from "@/src/services/rngService";
|
|
||||||
import { ExportNightwave, ExportRegions } from "warframe-public-export-plus";
|
|
||||||
import {
|
|
||||||
EPOCH,
|
|
||||||
getSortieTime,
|
|
||||||
missionTags,
|
|
||||||
sortieBosses,
|
|
||||||
sortieBossNode,
|
|
||||||
sortieBossToFaction,
|
|
||||||
sortieFactionToFactionIndexes,
|
|
||||||
sortieFactionToSystemIndexes
|
|
||||||
} from "@/src/helpers/worlstateHelper";
|
|
||||||
|
|
||||||
export const worldStateController: RequestHandler = (req, res) => {
|
export const worldStateController: RequestHandler = (req, res) => {
|
||||||
const day = Math.trunc((Date.now() - EPOCH) / 86400000);
|
res.json(getWorldState(req.query.buildLabel as string | undefined));
|
||||||
const week = Math.trunc(day / 7);
|
|
||||||
const weekStart = EPOCH + week * 604800000;
|
|
||||||
const weekEnd = weekStart + 604800000;
|
|
||||||
|
|
||||||
const worldState: IWorldState = {
|
|
||||||
BuildLabel:
|
|
||||||
typeof req.query.buildLabel == "string"
|
|
||||||
? req.query.buildLabel.split(" ").join("+")
|
|
||||||
: buildConfig.buildLabel,
|
|
||||||
Time: config.worldState?.lockTime || Math.round(Date.now() / 1000),
|
|
||||||
Goals: [],
|
|
||||||
GlobalUpgrades: [],
|
|
||||||
Sorties: [],
|
|
||||||
LiteSorties: [],
|
|
||||||
EndlessXpChoices: [],
|
|
||||||
SeasonInfo: {
|
|
||||||
Activation: { $date: { $numberLong: "1715796000000" } },
|
|
||||||
Expiry: { $date: { $numberLong: "2000000000000" } },
|
|
||||||
AffiliationTag: "RadioLegionIntermission12Syndicate",
|
|
||||||
Season: 14,
|
|
||||||
Phase: 0,
|
|
||||||
Params: "",
|
|
||||||
ActiveChallenges: [
|
|
||||||
getSeasonDailyChallenge(day - 2),
|
|
||||||
getSeasonDailyChallenge(day - 1),
|
|
||||||
getSeasonDailyChallenge(day - 0),
|
|
||||||
getSeasonWeeklyChallenge(week, 0),
|
|
||||||
getSeasonWeeklyChallenge(week, 1),
|
|
||||||
getSeasonWeeklyHardChallenge(week, 2),
|
|
||||||
getSeasonWeeklyHardChallenge(week, 3),
|
|
||||||
{
|
|
||||||
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 0).toString().padStart(8, "0") },
|
|
||||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
|
||||||
Challenge:
|
|
||||||
"/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentCompleteMissions" + (week - 12)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 1).toString().padStart(8, "0") },
|
|
||||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
|
||||||
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEximus" + (week - 12)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: { $oid: "67e1b96e9d00cb47" + (week * 7 + 2).toString().padStart(8, "0") },
|
|
||||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
|
||||||
Challenge: "/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanentKillEnemies" + (week - 12)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
...staticWorldState
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.worldState?.starDays) {
|
|
||||||
worldState.Goals.push({
|
|
||||||
_id: { $oid: "67a4dcce2a198564d62e1647" },
|
|
||||||
Activation: { $date: { $numberLong: "1738868400000" } },
|
|
||||||
Expiry: { $date: { $numberLong: "2000000000000" } },
|
|
||||||
Count: 0,
|
|
||||||
Goal: 0,
|
|
||||||
Success: 0,
|
|
||||||
Personal: true,
|
|
||||||
Desc: "/Lotus/Language/Events/ValentinesFortunaName",
|
|
||||||
ToolTip: "/Lotus/Language/Events/ValentinesFortunaName",
|
|
||||||
Icon: "/Lotus/Interface/Icons/WorldStatePanel/ValentinesEventIcon.png",
|
|
||||||
Tag: "FortunaValentines",
|
|
||||||
Node: "SolarisUnitedHub1"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elite Sanctuary Onslaught cycling every week
|
|
||||||
worldState.NodeOverrides.find(x => x.Node == "SolNode802")!.Seed = week; // unfaithful
|
|
||||||
|
|
||||||
// Holdfast, Cavia, & Hex bounties cycling every 2.5 hours; unfaithful implementation
|
|
||||||
const bountyCycle = Math.trunc(Date.now() / 9000000);
|
|
||||||
const bountyCycleStart = bountyCycle * 9000000;
|
|
||||||
const bountyCycleEnd = bountyCycleStart + 9000000;
|
|
||||||
worldState.SyndicateMissions[worldState.SyndicateMissions.findIndex(x => x.Tag == "ZarimanSyndicate")] = {
|
|
||||||
_id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000029" },
|
|
||||||
Activation: { $date: { $numberLong: bountyCycleStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString() } },
|
|
||||||
Tag: "ZarimanSyndicate",
|
|
||||||
Seed: bountyCycle,
|
|
||||||
Nodes: []
|
|
||||||
};
|
|
||||||
worldState.SyndicateMissions[worldState.SyndicateMissions.findIndex(x => x.Tag == "EntratiLabSyndicate")] = {
|
|
||||||
_id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000004" },
|
|
||||||
Activation: { $date: { $numberLong: bountyCycleStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString() } },
|
|
||||||
Tag: "EntratiLabSyndicate",
|
|
||||||
Seed: bountyCycle,
|
|
||||||
Nodes: []
|
|
||||||
};
|
|
||||||
worldState.SyndicateMissions[worldState.SyndicateMissions.findIndex(x => x.Tag == "HexSyndicate")] = {
|
|
||||||
_id: { $oid: Math.trunc(bountyCycleStart / 1000).toString(16) + "0000000000000006" },
|
|
||||||
Activation: { $date: { $numberLong: bountyCycleStart.toString(10) } },
|
|
||||||
Expiry: { $date: { $numberLong: bountyCycleEnd.toString(10) } },
|
|
||||||
Tag: "HexSyndicate",
|
|
||||||
Seed: bountyCycle,
|
|
||||||
Nodes: []
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.worldState?.creditBoost) {
|
|
||||||
worldState.GlobalUpgrades.push({
|
|
||||||
_id: { $oid: "5b23106f283a555109666672" },
|
|
||||||
Activation: { $date: { $numberLong: "1740164400000" } },
|
|
||||||
ExpiryDate: { $date: { $numberLong: "2000000000000" } },
|
|
||||||
UpgradeType: "GAMEPLAY_MONEY_REWARD_AMOUNT",
|
|
||||||
OperationType: "MULTIPLY",
|
|
||||||
Value: 2,
|
|
||||||
LocalizeTag: "",
|
|
||||||
LocalizeDescTag: ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (config.worldState?.affinityBoost) {
|
|
||||||
worldState.GlobalUpgrades.push({
|
|
||||||
_id: { $oid: "5b23106f283a555109666673" },
|
|
||||||
Activation: { $date: { $numberLong: "1740164400000" } },
|
|
||||||
ExpiryDate: { $date: { $numberLong: "2000000000000" } },
|
|
||||||
UpgradeType: "GAMEPLAY_KILL_XP_AMOUNT",
|
|
||||||
OperationType: "MULTIPLY",
|
|
||||||
Value: 2,
|
|
||||||
LocalizeTag: "",
|
|
||||||
LocalizeDescTag: ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (config.worldState?.resourceBoost) {
|
|
||||||
worldState.GlobalUpgrades.push({
|
|
||||||
_id: { $oid: "5b23106f283a555109666674" },
|
|
||||||
Activation: { $date: { $numberLong: "1740164400000" } },
|
|
||||||
ExpiryDate: { $date: { $numberLong: "2000000000000" } },
|
|
||||||
UpgradeType: "GAMEPLAY_PICKUP_AMOUNT",
|
|
||||||
OperationType: "MULTIPLY",
|
|
||||||
Value: 2,
|
|
||||||
LocalizeTag: "",
|
|
||||||
LocalizeDescTag: ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sortie cycling every day
|
|
||||||
{
|
|
||||||
let genDay;
|
|
||||||
let dayStart;
|
|
||||||
let dayEnd;
|
|
||||||
const sortieRolloverToday = getSortieTime(day);
|
|
||||||
if (Date.now() < sortieRolloverToday) {
|
|
||||||
// Early in the day, generate sortie for `day - 1`, expiring at `sortieRolloverToday`.
|
|
||||||
genDay = day - 1;
|
|
||||||
dayStart = getSortieTime(genDay);
|
|
||||||
dayEnd = sortieRolloverToday;
|
|
||||||
} else {
|
|
||||||
// Late in the day, generate sortie for `day`, expiring at `getSortieTime(day + 1)`.
|
|
||||||
genDay = day;
|
|
||||||
dayStart = sortieRolloverToday;
|
|
||||||
dayEnd = getSortieTime(day + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rng = new CRng(genDay);
|
|
||||||
|
|
||||||
const boss = rng.randomElement(sortieBosses);
|
|
||||||
|
|
||||||
const modifiers = [
|
|
||||||
"SORTIE_MODIFIER_LOW_ENERGY",
|
|
||||||
"SORTIE_MODIFIER_IMPACT",
|
|
||||||
"SORTIE_MODIFIER_SLASH",
|
|
||||||
"SORTIE_MODIFIER_PUNCTURE",
|
|
||||||
"SORTIE_MODIFIER_EXIMUS",
|
|
||||||
"SORTIE_MODIFIER_MAGNETIC",
|
|
||||||
"SORTIE_MODIFIER_CORROSIVE",
|
|
||||||
"SORTIE_MODIFIER_VIRAL",
|
|
||||||
"SORTIE_MODIFIER_ELECTRICITY",
|
|
||||||
"SORTIE_MODIFIER_RADIATION",
|
|
||||||
"SORTIE_MODIFIER_GAS",
|
|
||||||
"SORTIE_MODIFIER_FIRE",
|
|
||||||
"SORTIE_MODIFIER_EXPLOSION",
|
|
||||||
"SORTIE_MODIFIER_FREEZE",
|
|
||||||
"SORTIE_MODIFIER_TOXIN",
|
|
||||||
"SORTIE_MODIFIER_POISON",
|
|
||||||
"SORTIE_MODIFIER_HAZARD_RADIATION",
|
|
||||||
"SORTIE_MODIFIER_HAZARD_MAGNETIC",
|
|
||||||
"SORTIE_MODIFIER_HAZARD_FOG", // TODO: push this if the mission tileset is Grineer Forest
|
|
||||||
"SORTIE_MODIFIER_HAZARD_FIRE", // TODO: push this if the mission tileset is Corpus Ship or Grineer Galleon
|
|
||||||
"SORTIE_MODIFIER_HAZARD_ICE",
|
|
||||||
"SORTIE_MODIFIER_HAZARD_COLD",
|
|
||||||
"SORTIE_MODIFIER_SECONDARY_ONLY",
|
|
||||||
"SORTIE_MODIFIER_SHOTGUN_ONLY",
|
|
||||||
"SORTIE_MODIFIER_SNIPER_ONLY",
|
|
||||||
"SORTIE_MODIFIER_RIFLE_ONLY",
|
|
||||||
"SORTIE_MODIFIER_MELEE_ONLY",
|
|
||||||
"SORTIE_MODIFIER_BOW_ONLY"
|
|
||||||
];
|
|
||||||
|
|
||||||
if (sortieBossToFaction[boss] == "FC_CORPUS") modifiers.push("SORTIE_MODIFIER_SHIELDS");
|
|
||||||
if (sortieBossToFaction[boss] != "FC_CORPUS") modifiers.push("SORTIE_MODIFIER_ARMOR");
|
|
||||||
|
|
||||||
const nodes: string[] = [];
|
|
||||||
const availableMissionIndexes: number[] = [];
|
|
||||||
for (const [key, value] of Object.entries(ExportRegions)) {
|
|
||||||
if (
|
|
||||||
sortieFactionToSystemIndexes[sortieBossToFaction[boss]].includes(value.systemIndex) &&
|
|
||||||
sortieFactionToFactionIndexes[sortieBossToFaction[boss]].includes(value.factionIndex!) &&
|
|
||||||
value.name.indexOf("Archwing") == -1 &&
|
|
||||||
value.missionIndex != 0 && // Exclude MT_ASSASSINATION
|
|
||||||
value.missionIndex != 5 && // Exclude MT_CAPTURE
|
|
||||||
value.missionIndex != 21 && // Exclude MT_PURIFY
|
|
||||||
value.missionIndex != 23 && // Exclude MT_JUNCTION
|
|
||||||
value.missionIndex <= 28
|
|
||||||
) {
|
|
||||||
if (!availableMissionIndexes.includes(value.missionIndex)) {
|
|
||||||
availableMissionIndexes.push(value.missionIndex);
|
|
||||||
}
|
|
||||||
nodes.push(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedNodes: { missionType: string; modifierType: string; node: string }[] = [];
|
|
||||||
const missionTypes = new Set();
|
|
||||||
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
const randomIndex = rng.randomInt(0, nodes.length - 1);
|
|
||||||
const node = nodes[randomIndex];
|
|
||||||
let missionIndex = ExportRegions[node].missionIndex;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!["SolNode404", "SolNode411"].includes(node) && // for some reason the game doesn't like missionType changes for these missions
|
|
||||||
missionIndex != 28 &&
|
|
||||||
rng.randomInt(0, 2) == 2
|
|
||||||
) {
|
|
||||||
missionIndex = rng.randomElement(availableMissionIndexes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 2 && rng.randomInt(0, 2) == 2) {
|
|
||||||
const filteredModifiers = modifiers.filter(mod => mod !== "SORTIE_MODIFIER_MELEE_ONLY");
|
|
||||||
const modifierType = rng.randomElement(filteredModifiers);
|
|
||||||
|
|
||||||
if (boss == "SORTIE_BOSS_PHORID") {
|
|
||||||
selectedNodes.push({ missionType: "MT_ASSASSINATION", modifierType, node });
|
|
||||||
nodes.splice(randomIndex, 1);
|
|
||||||
continue;
|
|
||||||
} else if (sortieBossNode[boss]) {
|
|
||||||
selectedNodes.push({ missionType: "MT_ASSASSINATION", modifierType, node: sortieBossNode[boss] });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const missionType = missionTags[missionIndex];
|
|
||||||
|
|
||||||
if (missionTypes.has(missionType)) {
|
|
||||||
i--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredModifiers =
|
|
||||||
missionType === "MT_TERRITORY"
|
|
||||||
? modifiers.filter(mod => mod != "SORTIE_MODIFIER_HAZARD_RADIATION")
|
|
||||||
: modifiers;
|
|
||||||
|
|
||||||
const modifierType = rng.randomElement(filteredModifiers);
|
|
||||||
|
|
||||||
selectedNodes.push({ missionType, modifierType, node });
|
|
||||||
nodes.splice(randomIndex, 1);
|
|
||||||
missionTypes.add(missionType);
|
|
||||||
}
|
|
||||||
|
|
||||||
worldState.Sorties.push({
|
|
||||||
_id: { $oid: Math.trunc(dayStart / 1000).toString(16) + "d4d932c97c0a3acd" },
|
|
||||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
|
||||||
Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards",
|
|
||||||
Seed: genDay,
|
|
||||||
Boss: boss,
|
|
||||||
Variants: selectedNodes
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Archon Hunt cycling every week
|
|
||||||
{
|
|
||||||
const boss = ["SORTIE_BOSS_AMAR", "SORTIE_BOSS_NIRA", "SORTIE_BOSS_BOREAL"][week % 3];
|
|
||||||
const showdownNode = ["SolNode99", "SolNode53", "SolNode24"][week % 3];
|
|
||||||
const systemIndex = [3, 4, 2][week % 3]; // Mars, Jupiter, Earth
|
|
||||||
|
|
||||||
const nodes: string[] = [];
|
|
||||||
for (const [key, value] of Object.entries(ExportRegions)) {
|
|
||||||
if (
|
|
||||||
value.systemIndex === systemIndex &&
|
|
||||||
value.factionIndex !== undefined &&
|
|
||||||
value.factionIndex < 2 &&
|
|
||||||
value.name.indexOf("Archwing") == -1 &&
|
|
||||||
value.missionIndex != 0 // Exclude MT_ASSASSINATION
|
|
||||||
) {
|
|
||||||
nodes.push(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rng = new CRng(week);
|
|
||||||
const firstNodeIndex = rng.randomInt(0, nodes.length - 1);
|
|
||||||
const firstNode = nodes[firstNodeIndex];
|
|
||||||
nodes.splice(firstNodeIndex, 1);
|
|
||||||
worldState.LiteSorties.push({
|
|
||||||
_id: {
|
|
||||||
$oid: Math.trunc(weekStart / 1000).toString(16) + "5e23a244740a190c"
|
|
||||||
},
|
|
||||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
|
||||||
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards",
|
|
||||||
Seed: week,
|
|
||||||
Boss: boss,
|
|
||||||
Missions: [
|
|
||||||
{
|
|
||||||
missionType: rng.randomElement([
|
|
||||||
"MT_INTEL",
|
|
||||||
"MT_MOBILE_DEFENSE",
|
|
||||||
"MT_EXTERMINATION",
|
|
||||||
"MT_SABOTAGE",
|
|
||||||
"MT_RESCUE"
|
|
||||||
]),
|
|
||||||
node: firstNode
|
|
||||||
},
|
|
||||||
{
|
|
||||||
missionType: rng.randomElement([
|
|
||||||
"MT_DEFENSE",
|
|
||||||
"MT_TERRITORY",
|
|
||||||
"MT_ARTIFACT",
|
|
||||||
"MT_EXCAVATE",
|
|
||||||
"MT_SURVIVAL"
|
|
||||||
]),
|
|
||||||
node: rng.randomElement(nodes)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
missionType: "MT_ASSASSINATION",
|
|
||||||
node: showdownNode
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Circuit choices cycling every week
|
|
||||||
worldState.EndlessXpChoices.push({
|
|
||||||
Category: "EXC_NORMAL",
|
|
||||||
Choices: [
|
|
||||||
["Nidus", "Octavia", "Harrow"],
|
|
||||||
["Gara", "Khora", "Revenant"],
|
|
||||||
["Garuda", "Baruuk", "Hildryn"],
|
|
||||||
["Excalibur", "Trinity", "Ember"],
|
|
||||||
["Loki", "Mag", "Rhino"],
|
|
||||||
["Ash", "Frost", "Nyx"],
|
|
||||||
["Saryn", "Vauban", "Nova"],
|
|
||||||
["Nekros", "Valkyr", "Oberon"],
|
|
||||||
["Hydroid", "Mirage", "Limbo"],
|
|
||||||
["Mesa", "Chroma", "Atlas"],
|
|
||||||
["Ivara", "Inaros", "Titania"]
|
|
||||||
][week % 12]
|
|
||||||
});
|
|
||||||
worldState.EndlessXpChoices.push({
|
|
||||||
Category: "EXC_HARD",
|
|
||||||
Choices: [
|
|
||||||
["Boar", "Gammacor", "Angstrum", "Gorgon", "Anku"],
|
|
||||||
["Bo", "Latron", "Furis", "Furax", "Strun"],
|
|
||||||
["Lex", "Magistar", "Boltor", "Bronco", "CeramicDagger"],
|
|
||||||
["Torid", "DualToxocyst", "DualIchor", "Miter", "Atomos"],
|
|
||||||
["AckAndBrunt", "Soma", "Vasto", "NamiSolo", "Burston"],
|
|
||||||
["Zylok", "Sibear", "Dread", "Despair", "Hate"],
|
|
||||||
["Dera", "Sybaris", "Cestra", "Sicarus", "Okina"],
|
|
||||||
["Braton", "Lato", "Skana", "Paris", "Kunai"]
|
|
||||||
][week % 8]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 1999 Calendar Season cycling every week + YearIteration every 4 weeks
|
|
||||||
worldState.KnownCalendarSeasons[0].Activation = { $date: { $numberLong: weekStart.toString() } };
|
|
||||||
worldState.KnownCalendarSeasons[0].Expiry = { $date: { $numberLong: weekEnd.toString() } };
|
|
||||||
worldState.KnownCalendarSeasons[0].Season = ["CST_WINTER", "CST_SPRING", "CST_SUMMER", "CST_FALL"][week % 4];
|
|
||||||
worldState.KnownCalendarSeasons[0].Days = [
|
|
||||||
static1999WinterDays,
|
|
||||||
static1999SpringDays,
|
|
||||||
static1999SummerDays,
|
|
||||||
static1999FallDays
|
|
||||||
][week % 4];
|
|
||||||
worldState.KnownCalendarSeasons[0].YearIteration = Math.trunc(week / 4);
|
|
||||||
|
|
||||||
// Sentient Anomaly cycling every 30 minutes
|
|
||||||
const halfHour = Math.trunc(Date.now() / (unixTimesInMs.hour / 2));
|
|
||||||
const tmp = {
|
|
||||||
cavabegin: "1690761600",
|
|
||||||
PurchasePlatformLockEnabled: true,
|
|
||||||
tcsn: true,
|
|
||||||
pgr: {
|
|
||||||
ts: "1732572900",
|
|
||||||
en: "CUSTOM DECALS @ ZEVILA",
|
|
||||||
fr: "DECALS CUSTOM @ ZEVILA",
|
|
||||||
it: "DECALCOMANIE PERSONALIZZATE @ ZEVILA",
|
|
||||||
de: "AUFKLEBER NACH WUNSCH @ ZEVILA",
|
|
||||||
es: "CALCOMANÍAS PERSONALIZADAS @ ZEVILA",
|
|
||||||
pt: "DECALQUES PERSONALIZADOS NA ZEVILA",
|
|
||||||
ru: "ПОЛЬЗОВАТЕЛЬСКИЕ НАКЛЕЙКИ @ ЗеВиЛа",
|
|
||||||
pl: "NOWE NAKLEJKI @ ZEVILA",
|
|
||||||
uk: "КОРИСТУВАЦЬКІ ДЕКОЛІ @ ЗІВІЛА",
|
|
||||||
tr: "ÖZEL ÇIKARTMALAR @ ZEVILA",
|
|
||||||
ja: "カスタムデカール @ ゼビラ",
|
|
||||||
zh: "定制贴花认准泽威拉",
|
|
||||||
ko: "커스텀 데칼 @ ZEVILA",
|
|
||||||
tc: "自訂貼花 @ ZEVILA",
|
|
||||||
th: "รูปลอกสั่งทำที่ ZEVILA"
|
|
||||||
},
|
|
||||||
ennnd: true,
|
|
||||||
mbrt: true,
|
|
||||||
sfn: [550, 553, 554, 555][halfHour % 4]
|
|
||||||
};
|
|
||||||
worldState.Tmp = JSON.stringify(tmp);
|
|
||||||
|
|
||||||
res.json(worldState);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IWorldState {
|
|
||||||
Version: number; // for goals
|
|
||||||
BuildLabel: string;
|
|
||||||
Time: number;
|
|
||||||
Goals: IGoal[];
|
|
||||||
SyndicateMissions: ISyndicateMission[];
|
|
||||||
GlobalUpgrades: IGlobalUpgrade[];
|
|
||||||
Sorties: ISortie[];
|
|
||||||
LiteSorties: ILiteSortie[];
|
|
||||||
NodeOverrides: INodeOverride[];
|
|
||||||
EndlessXpChoices: IEndlessXpChoice[];
|
|
||||||
SeasonInfo: {
|
|
||||||
Activation: IMongoDate;
|
|
||||||
Expiry: IMongoDate;
|
|
||||||
AffiliationTag: string;
|
|
||||||
Season: number;
|
|
||||||
Phase: number;
|
|
||||||
Params: string;
|
|
||||||
ActiveChallenges: ISeasonChallenge[];
|
|
||||||
};
|
|
||||||
KnownCalendarSeasons: ICalendarSeason[];
|
|
||||||
Tmp?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IGoal {
|
|
||||||
_id: IOid;
|
|
||||||
Activation: IMongoDate;
|
|
||||||
Expiry: IMongoDate;
|
|
||||||
Count: number;
|
|
||||||
Goal: number;
|
|
||||||
Success: number;
|
|
||||||
Personal: boolean;
|
|
||||||
Desc: string;
|
|
||||||
ToolTip: string;
|
|
||||||
Icon: string;
|
|
||||||
Tag: string;
|
|
||||||
Node: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISyndicateMission {
|
|
||||||
_id: IOid;
|
|
||||||
Activation: IMongoDate;
|
|
||||||
Expiry: IMongoDate;
|
|
||||||
Tag: string;
|
|
||||||
Seed: number;
|
|
||||||
Nodes: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IGlobalUpgrade {
|
|
||||||
_id: IOid;
|
|
||||||
Activation: IMongoDate;
|
|
||||||
ExpiryDate: IMongoDate;
|
|
||||||
UpgradeType: string;
|
|
||||||
OperationType: string;
|
|
||||||
Value: number;
|
|
||||||
LocalizeTag: string;
|
|
||||||
LocalizeDescTag: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface INodeOverride {
|
|
||||||
_id: IOid;
|
|
||||||
Activation?: IMongoDate;
|
|
||||||
Expiry?: IMongoDate;
|
|
||||||
Node: string;
|
|
||||||
Hide?: boolean;
|
|
||||||
Seed?: number;
|
|
||||||
LevelOverride?: string;
|
|
||||||
Faction?: string;
|
|
||||||
CustomNpcEncounters?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISortie {
|
|
||||||
_id: IOid;
|
|
||||||
Activation: IMongoDate;
|
|
||||||
Expiry: IMongoDate;
|
|
||||||
Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards";
|
|
||||||
Seed: number;
|
|
||||||
Boss: string;
|
|
||||||
Variants: {
|
|
||||||
missionType: string;
|
|
||||||
modifierType: string;
|
|
||||||
node: string;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ILiteSortie {
|
|
||||||
_id: IOid;
|
|
||||||
Activation: IMongoDate;
|
|
||||||
Expiry: IMongoDate;
|
|
||||||
Reward: "/Lotus/Types/Game/MissionDecks/ArchonSortieRewards";
|
|
||||||
Seed: number;
|
|
||||||
Boss: string; // "SORTIE_BOSS_AMAR" | "SORTIE_BOSS_NIRA" | "SORTIE_BOSS_BOREAL"
|
|
||||||
Missions: {
|
|
||||||
missionType: string;
|
|
||||||
node: string;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IEndlessXpChoice {
|
|
||||||
Category: string;
|
|
||||||
Choices: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISeasonChallenge {
|
|
||||||
_id: IOid;
|
|
||||||
Daily?: boolean;
|
|
||||||
Activation: IMongoDate;
|
|
||||||
Expiry: IMongoDate;
|
|
||||||
Challenge: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ICalendarSeason {
|
|
||||||
Activation: IMongoDate;
|
|
||||||
Expiry: IMongoDate;
|
|
||||||
Season: string; // "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"
|
|
||||||
Days: {
|
|
||||||
day: number;
|
|
||||||
}[];
|
|
||||||
YearIteration: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dailyChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
|
|
||||||
x.startsWith("/Lotus/Types/Challenges/Seasons/Daily/")
|
|
||||||
);
|
|
||||||
|
|
||||||
const getSeasonDailyChallenge = (day: number): ISeasonChallenge => {
|
|
||||||
const dayStart = EPOCH + day * 86400000;
|
|
||||||
const dayEnd = EPOCH + (day + 3) * 86400000;
|
|
||||||
const rng = new CRng(day);
|
|
||||||
return {
|
|
||||||
_id: { $oid: "67e1b5ca9d00cb47" + day.toString().padStart(8, "0") },
|
|
||||||
Daily: true,
|
|
||||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
|
||||||
Challenge: rng.randomElement(dailyChallenges)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const weeklyChallenges = Object.keys(ExportNightwave.challenges).filter(
|
|
||||||
x =>
|
|
||||||
x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/") &&
|
|
||||||
!x.startsWith("/Lotus/Types/Challenges/Seasons/Weekly/SeasonWeeklyPermanent")
|
|
||||||
);
|
|
||||||
|
|
||||||
const getSeasonWeeklyChallenge = (week: number, id: number): ISeasonChallenge => {
|
|
||||||
const weekStart = EPOCH + week * 604800000;
|
|
||||||
const weekEnd = weekStart + 604800000;
|
|
||||||
const challengeId = week * 7 + id;
|
|
||||||
const rng = new CRng(challengeId);
|
|
||||||
return {
|
|
||||||
_id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
|
|
||||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
|
||||||
Challenge: rng.randomElement(weeklyChallenges)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const weeklyHardChallenges = Object.keys(ExportNightwave.challenges).filter(x =>
|
|
||||||
x.startsWith("/Lotus/Types/Challenges/Seasons/WeeklyHard/")
|
|
||||||
);
|
|
||||||
|
|
||||||
const getSeasonWeeklyHardChallenge = (week: number, id: number): ISeasonChallenge => {
|
|
||||||
const weekStart = EPOCH + week * 604800000;
|
|
||||||
const weekEnd = weekStart + 604800000;
|
|
||||||
const challengeId = week * 7 + id;
|
|
||||||
const rng = new CRng(challengeId);
|
|
||||||
return {
|
|
||||||
_id: { $oid: "67e1bb2d9d00cb47" + challengeId.toString().padStart(8, "0") },
|
|
||||||
Activation: { $date: { $numberLong: weekStart.toString() } },
|
|
||||||
Expiry: { $date: { $numberLong: weekEnd.toString() } },
|
|
||||||
Challenge: rng.randomElement(weeklyHardChallenges)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
@ -27,7 +27,15 @@ const viewController: RequestHandler = async (req, res) => {
|
|||||||
for (const type of Object.keys(ExportEnemies.avatars)) {
|
for (const type of Object.keys(ExportEnemies.avatars)) {
|
||||||
if (!scans.has(type)) scans.add(type);
|
if (!scans.has(type)) scans.add(type);
|
||||||
}
|
}
|
||||||
responseJson.Scans ??= [];
|
|
||||||
|
// Take any existing scans and also set them to 9999
|
||||||
|
if (responseJson.Scans) {
|
||||||
|
for (const scan of responseJson.Scans) {
|
||||||
|
scans.add(scan.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseJson.Scans = [];
|
||||||
|
|
||||||
for (const type of scans) {
|
for (const type of scans) {
|
||||||
responseJson.Scans.push({ type: type, scans: 9999 });
|
responseJson.Scans.push({ type: type, scans: 9999 });
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
|
import { TRarity } from "warframe-public-export-plus";
|
||||||
|
|
||||||
export const toOid = (objectId: Types.ObjectId): IOid => {
|
export const toOid = (objectId: Types.ObjectId): IOid => {
|
||||||
return { $oid: objectId.toString() } satisfies IOid;
|
return { $oid: objectId.toString() } satisfies IOid;
|
||||||
@ -8,3 +9,148 @@ export const toOid = (objectId: Types.ObjectId): IOid => {
|
|||||||
export const toMongoDate = (date: Date): IMongoDate => {
|
export const toMongoDate = (date: Date): IMongoDate => {
|
||||||
return { $date: { $numberLong: date.getTime().toString() } };
|
return { $date: { $numberLong: date.getTime().toString() } };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fromMongoData = (date: IMongoDate): Date => {
|
||||||
|
return new Date(parseInt(date.$date.$numberLong));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const kubrowWeights: Record<TRarity, number> = {
|
||||||
|
COMMON: 6,
|
||||||
|
UNCOMMON: 4,
|
||||||
|
RARE: 2,
|
||||||
|
LEGENDARY: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
export const kubrowFurPatternsWeights: Record<TRarity, number> = {
|
||||||
|
COMMON: 6,
|
||||||
|
UNCOMMON: 5,
|
||||||
|
RARE: 2,
|
||||||
|
LEGENDARY: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
export const catbrowDetails = {
|
||||||
|
Colors: [
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseA", rarity: "COMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseB", rarity: "COMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseC", rarity: "COMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorBaseD", rarity: "COMMON" as TRarity },
|
||||||
|
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryA", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryB", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryC", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorSecondaryD", rarity: "UNCOMMON" as TRarity },
|
||||||
|
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryA", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryB", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryC", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorTertiaryD", rarity: "RARE" as TRarity },
|
||||||
|
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsA", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsB", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsC", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorAccentsD", rarity: "LEGENDARY" as TRarity }
|
||||||
|
],
|
||||||
|
|
||||||
|
EyeColors: [
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesA", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesB", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesC", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesD", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesE", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesF", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesG", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesH", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesI", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesJ", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesK", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesL", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesM", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Colors/CatbrowPetColorEyesN", rarity: "LEGENDARY" as TRarity }
|
||||||
|
],
|
||||||
|
|
||||||
|
FurPatterns: [{ type: "/Lotus/Types/Game/CatbrowPet/Patterns/CatbrowPetPatternA", rarity: "COMMON" as TRarity }],
|
||||||
|
|
||||||
|
BodyTypes: [
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/BodyTypes/CatbrowPetRegularBodyType", rarity: "LEGENDARY" as TRarity }
|
||||||
|
],
|
||||||
|
|
||||||
|
Heads: [
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadA", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadB", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadC", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Heads/CatbrowHeadD", rarity: "LEGENDARY" as TRarity }
|
||||||
|
],
|
||||||
|
|
||||||
|
Tails: [
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailA", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailB", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailC", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailD", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/CatbrowPet/Tails/CatbrowTailE", rarity: "LEGENDARY" as TRarity }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const kubrowDetails = {
|
||||||
|
Colors: [
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneA", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneB", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneC", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneD", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneE", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneF", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneG", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMundaneH", rarity: "UNCOMMON" as TRarity },
|
||||||
|
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidA", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidB", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidC", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidD", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidE", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidF", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidG", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorMidH", rarity: "RARE" as TRarity },
|
||||||
|
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantA", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantB", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantC", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantD", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantE", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantF", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantG", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorVibrantH", rarity: "LEGENDARY" as TRarity }
|
||||||
|
],
|
||||||
|
|
||||||
|
EyeColors: [
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesA", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesB", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesC", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesD", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesE", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesF", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesG", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesH", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Colors/KubrowPetColorEyesI", rarity: "LEGENDARY" as TRarity }
|
||||||
|
],
|
||||||
|
|
||||||
|
FurPatterns: [
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternB", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternA", rarity: "UNCOMMON" as TRarity },
|
||||||
|
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternC", rarity: "RARE" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternD", rarity: "RARE" as TRarity },
|
||||||
|
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternE", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/Patterns/KubrowPetPatternF", rarity: "LEGENDARY" as TRarity }
|
||||||
|
],
|
||||||
|
|
||||||
|
BodyTypes: [
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetRegularBodyType", rarity: "UNCOMMON" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetHeavyBodyType", rarity: "LEGENDARY" as TRarity },
|
||||||
|
{ type: "/Lotus/Types/Game/KubrowPet/BodyTypes/KubrowPetThinBodyType", rarity: "LEGENDARY" as TRarity }
|
||||||
|
],
|
||||||
|
|
||||||
|
Heads: [],
|
||||||
|
|
||||||
|
Tails: []
|
||||||
|
};
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import { ExportRegions } from "warframe-public-export-plus";
|
import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
|
||||||
import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { IInfNode, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
import { SRng } from "@/src/services/rngService";
|
import { getRewardAtPercentage, SRng } from "@/src/services/rngService";
|
||||||
|
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
|
||||||
|
import { logger } from "../utils/logger";
|
||||||
|
import { IOid } from "../types/commonTypes";
|
||||||
|
import { Types } from "mongoose";
|
||||||
|
import { addMods, generateRewardSeed } from "../services/inventoryService";
|
||||||
|
import { isArchwingMission } from "../services/worldStateService";
|
||||||
|
import { fromStoreItem, toStoreItem } from "../services/itemDataService";
|
||||||
|
import { createMessage } from "../services/inboxService";
|
||||||
|
|
||||||
export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
|
export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
|
||||||
const infNodes = [];
|
const infNodes = [];
|
||||||
@ -17,7 +25,7 @@ export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
|
|||||||
value.missionIndex != 42 && // not face off
|
value.missionIndex != 42 && // not face off
|
||||||
value.name.indexOf("1999NodeI") == -1 && // not stage defence
|
value.name.indexOf("1999NodeI") == -1 && // not stage defence
|
||||||
value.name.indexOf("1999NodeJ") == -1 && // not lich bounty
|
value.name.indexOf("1999NodeJ") == -1 && // not lich bounty
|
||||||
value.name.indexOf("Archwing") == -1
|
!isArchwingMission(value)
|
||||||
) {
|
) {
|
||||||
//console.log(dict_en[value.name]);
|
//console.log(dict_en[value.name]);
|
||||||
infNodes.push({ Node: key, Influence: 1 });
|
infNodes.push({ Node: key, Influence: 1 });
|
||||||
@ -32,13 +40,339 @@ const systemIndexes: Record<string, number[]> = {
|
|||||||
FC_INFESTATION: [23]
|
FC_INFESTATION: [23]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const showdownNodes: Record<string, string> = {
|
||||||
|
FC_GRINEER: "CrewBattleNode557",
|
||||||
|
FC_CORPUS: "CrewBattleNode558",
|
||||||
|
FC_INFESTATION: "CrewBattleNode559"
|
||||||
|
};
|
||||||
|
|
||||||
// Get a parazon 'passcode' based on the nemesis fingerprint so it's always the same for the same nemesis.
|
// Get a parazon 'passcode' based on the nemesis fingerprint so it's always the same for the same nemesis.
|
||||||
export const getNemesisPasscode = (fp: bigint, faction: string): number[] => {
|
export const getNemesisPasscode = (nemesis: { fp: bigint; Faction: string }): number[] => {
|
||||||
const rng = new SRng(fp);
|
const rng = new SRng(nemesis.fp);
|
||||||
const passcode = [rng.randomInt(0, 7)];
|
const choices = [0, 1, 2, 3, 5, 6, 7];
|
||||||
if (faction != "FC_INFESTATION") {
|
let choiceIndex = rng.randomInt(0, choices.length - 1);
|
||||||
passcode.push(rng.randomInt(0, 7));
|
const passcode = [choices[choiceIndex]];
|
||||||
passcode.push(rng.randomInt(0, 7));
|
if (nemesis.Faction != "FC_INFESTATION") {
|
||||||
|
choices.splice(choiceIndex, 1);
|
||||||
|
choiceIndex = rng.randomInt(0, choices.length - 1);
|
||||||
|
passcode.push(choices[choiceIndex]);
|
||||||
|
|
||||||
|
choices.splice(choiceIndex, 1);
|
||||||
|
choiceIndex = rng.randomInt(0, choices.length - 1);
|
||||||
|
passcode.push(choices[choiceIndex]);
|
||||||
}
|
}
|
||||||
return passcode;
|
return passcode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reqiuemMods: readonly string[] = [
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalOneMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalTwoMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalThreeMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalFourMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalFiveMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalSixMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalSevenMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/ImmortalEightMod"
|
||||||
|
];
|
||||||
|
|
||||||
|
const antivirusMods: readonly string[] = [
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusOneMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusTwoMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusThreeMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusFourMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusFiveMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusSixMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusSevenMod",
|
||||||
|
"/Lotus/Upgrades/Mods/Immortal/AntivirusEightMod"
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getNemesisPasscodeModTypes = (nemesis: { fp: bigint; Faction: string }): string[] => {
|
||||||
|
const passcode = getNemesisPasscode(nemesis);
|
||||||
|
return nemesis.Faction == "FC_INFESTATION"
|
||||||
|
? passcode.map(i => antivirusMods[i])
|
||||||
|
: passcode.map(i => reqiuemMods[i]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const encodeNemesisGuess = (
|
||||||
|
symbol1: number,
|
||||||
|
result1: number,
|
||||||
|
symbol2: number,
|
||||||
|
result2: number,
|
||||||
|
symbol3: number,
|
||||||
|
result3: number
|
||||||
|
): number => {
|
||||||
|
return (
|
||||||
|
(symbol1 & 0xf) |
|
||||||
|
((result1 & 3) << 12) |
|
||||||
|
((symbol2 << 4) & 0xff) |
|
||||||
|
((result2 << 14) & 0xffff) |
|
||||||
|
((symbol3 & 0xf) << 8) |
|
||||||
|
((result3 & 3) << 16)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const decodeNemesisGuess = (val: number): number[] => {
|
||||||
|
return [val & 0xf, (val >> 12) & 3, (val & 0xff) >> 4, (val & 0xffff) >> 14, (val >> 8) & 0xf, (val >> 16) & 3];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface IKnifeResponse {
|
||||||
|
UpgradeIds?: string[];
|
||||||
|
UpgradeTypes?: string[];
|
||||||
|
UpgradeFingerprints?: { lvl: number }[];
|
||||||
|
UpgradeNew?: boolean[];
|
||||||
|
HasKnife?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getKnifeUpgrade = (
|
||||||
|
inventory: TInventoryDatabaseDocument,
|
||||||
|
dataknifeUpgrades: string[],
|
||||||
|
type: string
|
||||||
|
): { ItemId: IOid; ItemType: string } => {
|
||||||
|
if (dataknifeUpgrades.indexOf(type) != -1) {
|
||||||
|
return {
|
||||||
|
ItemId: { $oid: "000000000000000000000000" },
|
||||||
|
ItemType: type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (const upgradeId of dataknifeUpgrades) {
|
||||||
|
if (upgradeId.length == 24) {
|
||||||
|
const upgrade = inventory.Upgrades.id(upgradeId);
|
||||||
|
if (upgrade && upgrade.ItemType == type) {
|
||||||
|
return {
|
||||||
|
ItemId: { $oid: upgradeId },
|
||||||
|
ItemType: type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(`${type} does not seem to be installed on parazon?!`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const consumeModCharge = (
|
||||||
|
response: IKnifeResponse,
|
||||||
|
inventory: TInventoryDatabaseDocument,
|
||||||
|
upgrade: { ItemId: IOid; ItemType: string },
|
||||||
|
dataknifeUpgrades: string[]
|
||||||
|
): void => {
|
||||||
|
response.UpgradeIds ??= [];
|
||||||
|
response.UpgradeTypes ??= [];
|
||||||
|
response.UpgradeFingerprints ??= [];
|
||||||
|
response.UpgradeNew ??= [];
|
||||||
|
response.HasKnife = true;
|
||||||
|
|
||||||
|
if (upgrade.ItemId.$oid != "000000000000000000000000") {
|
||||||
|
const dbUpgrade = inventory.Upgrades.id(upgrade.ItemId.$oid)!;
|
||||||
|
const fingerprint = JSON.parse(dbUpgrade.UpgradeFingerprint!) as { lvl: number };
|
||||||
|
fingerprint.lvl += 1;
|
||||||
|
dbUpgrade.UpgradeFingerprint = JSON.stringify(fingerprint);
|
||||||
|
|
||||||
|
response.UpgradeIds.push(upgrade.ItemId.$oid);
|
||||||
|
response.UpgradeTypes.push(upgrade.ItemType);
|
||||||
|
response.UpgradeFingerprints.push(fingerprint);
|
||||||
|
response.UpgradeNew.push(false);
|
||||||
|
} else {
|
||||||
|
const id = new Types.ObjectId();
|
||||||
|
inventory.Upgrades.push({
|
||||||
|
_id: id,
|
||||||
|
ItemType: upgrade.ItemType,
|
||||||
|
UpgradeFingerprint: `{"lvl":1}`
|
||||||
|
});
|
||||||
|
|
||||||
|
addMods(inventory, [
|
||||||
|
{
|
||||||
|
ItemType: upgrade.ItemType,
|
||||||
|
ItemCount: -1
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const dataknifeRawUpgradeIndex = dataknifeUpgrades.indexOf(upgrade.ItemType);
|
||||||
|
if (dataknifeRawUpgradeIndex != -1) {
|
||||||
|
dataknifeUpgrades[dataknifeRawUpgradeIndex] = id.toString();
|
||||||
|
} else {
|
||||||
|
logger.warn(`${upgrade.ItemType} not found in dataknife config`);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.UpgradeIds.push(id.toString());
|
||||||
|
response.UpgradeTypes.push(upgrade.ItemType);
|
||||||
|
response.UpgradeFingerprints.push({ lvl: 1 });
|
||||||
|
response.UpgradeNew.push(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const kuvaLichVersionSixWeapons = [
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Drakgoon/KuvaDrakgoon",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Karak/KuvaKarak",
|
||||||
|
"/Lotus/Weapons/Grineer/Melee/GrnKuvaLichScythe/GrnKuvaLichScytheWeapon",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Kohm/KuvaKohm",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Ogris/KuvaOgris",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Quartakk/KuvaQuartakk",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Tonkor/KuvaTonkor",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Brakk/KuvaBrakk",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Kraken/KuvaKraken",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Seer/KuvaSeer",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Stubba/KuvaStubba",
|
||||||
|
"/Lotus/Weapons/Grineer/HeavyWeapons/GrnHeavyGrenadeLauncher",
|
||||||
|
"/Lotus/Weapons/Grineer/LongGuns/GrnKuvaLichRifle/GrnKuvaLichRifleWeapon",
|
||||||
|
"/Lotus/Weapons/Grineer/Bows/GrnBow/GrnBowWeapon",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hind/KuvaHind",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/Secondaries/Nukor/KuvaNukor",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Hek/KuvaHekWeapon",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Zarr/KuvaZarr",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/HeavyWeapons/Grattler/KuvaGrattler",
|
||||||
|
"/Lotus/Weapons/Grineer/KuvaLich/LongGuns/Sobek/KuvaSobek"
|
||||||
|
];
|
||||||
|
|
||||||
|
const corpusVersionThreeWeapons = [
|
||||||
|
"/Lotus/Weapons/Corpus/LongGuns/CrpBriefcaseLauncher/CrpBriefcaseLauncher",
|
||||||
|
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEArcaPlasmor/CrpBEArcaPlasmor",
|
||||||
|
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEFluxRifle/CrpBEFluxRifle",
|
||||||
|
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBETetra/CrpBETetra",
|
||||||
|
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBECycron/CrpBECycron",
|
||||||
|
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEDetron/CrpBEDetron",
|
||||||
|
"/Lotus/Weapons/Corpus/Pistols/CrpIgniterPistol/CrpIgniterPistol",
|
||||||
|
"/Lotus/Weapons/Corpus/Pistols/CrpBriefcaseAkimbo/CrpBriefcaseAkimboPistol",
|
||||||
|
"/Lotus/Weapons/Corpus/BoardExec/Secondary/CrpBEPlinx/CrpBEPlinxWeapon",
|
||||||
|
"/Lotus/Weapons/Corpus/BoardExec/Primary/CrpBEGlaxion/CrpBEGlaxion"
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getWeaponsForManifest = (manifest: string): readonly string[] => {
|
||||||
|
switch (manifest) {
|
||||||
|
case "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix":
|
||||||
|
return kuvaLichVersionSixWeapons;
|
||||||
|
case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree":
|
||||||
|
case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour":
|
||||||
|
return corpusVersionThreeWeapons;
|
||||||
|
}
|
||||||
|
throw new Error(`unknown nemesis manifest: ${manifest}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getInnateDamageTag = (
|
||||||
|
KillingSuit: string
|
||||||
|
):
|
||||||
|
| "InnateElectricityDamage"
|
||||||
|
| "InnateFreezeDamage"
|
||||||
|
| "InnateHeatDamage"
|
||||||
|
| "InnateImpactDamage"
|
||||||
|
| "InnateMagDamage"
|
||||||
|
| "InnateRadDamage"
|
||||||
|
| "InnateToxinDamage" => {
|
||||||
|
return ExportWarframes[KillingSuit].nemesisUpgradeTag!;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: For -1399275245665749231n, the value should be 75306944, but we're off by 59 with 75307003.
|
||||||
|
export const getInnateDamageValue = (fp: bigint): number => {
|
||||||
|
const rng = new SRng(fp);
|
||||||
|
rng.randomFloat(); // used for the weapon index
|
||||||
|
const WeaponUpgradeValueAttenuationExponent = 2.25;
|
||||||
|
let value = Math.pow(rng.randomFloat(), WeaponUpgradeValueAttenuationExponent);
|
||||||
|
if (value >= 0.941428) {
|
||||||
|
value = 1;
|
||||||
|
}
|
||||||
|
return Math.trunc(value * 0x40000000);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getKillTokenRewardCount = (fp: bigint): number => {
|
||||||
|
const rng = new SRng(fp);
|
||||||
|
return rng.randomInt(10, 15);
|
||||||
|
};
|
||||||
|
|
||||||
|
// /Lotus/Types/Enemies/InfestedLich/InfestedLichRewardManifest
|
||||||
|
const infestedLichRotA = [
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomHuman", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDJRomInfested", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitHuman", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyDrillbitInfested", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveHuman", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyHarddriveInfested", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketHuman", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyPacketInfested", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeHuman", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyZekeInfested", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterA", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandBillboardPosterB", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandDespairPoster", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandGridPoster", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandHuddlePoster", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandJumpPoster", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLimoPoster", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterDay", probability: 0.046 },
|
||||||
|
{
|
||||||
|
type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandLookingDownPosterNight",
|
||||||
|
probability: 0.045
|
||||||
|
},
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandSillyPoster", probability: 0.046 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhiteBluePoster", probability: 0.045 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/ShipDecos/BoybandPosters/BoybandWhitePinkPoster", probability: 0.045 }
|
||||||
|
];
|
||||||
|
const infestedLichRotB = [
|
||||||
|
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraA", probability: 0.072 },
|
||||||
|
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraB", probability: 0.071 },
|
||||||
|
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraC", probability: 0.072 },
|
||||||
|
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraD", probability: 0.071 },
|
||||||
|
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraE", probability: 0.072 },
|
||||||
|
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraF", probability: 0.071 },
|
||||||
|
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraG", probability: 0.071 },
|
||||||
|
{ type: "/Lotus/StoreItems/Upgrades/Skins/Effects/InfestedLichEphemeraH", probability: 0.072 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDJRomHype", probability: 0.071 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DancePacketWindmillShuffle", probability: 0.072 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceHarddrivePony", probability: 0.071 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceDrillbitCrisscross", probability: 0.072 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/Emotes/DanceZekeCanthavethis", probability: 0.071 },
|
||||||
|
{ type: "/Lotus/StoreItems/Types/Items/PhotoBooth/PhotoboothTileRJLasXStadiumBossArena", probability: 0.071 }
|
||||||
|
];
|
||||||
|
export const getInfestedLichItemRewards = (fp: bigint): string[] => {
|
||||||
|
const rng = new SRng(fp);
|
||||||
|
const rotAReward = getRewardAtPercentage(infestedLichRotA, rng.randomFloat())!.type;
|
||||||
|
rng.randomFloat(); // unused afaict
|
||||||
|
const rotBReward = getRewardAtPercentage(infestedLichRotB, rng.randomFloat())!.type;
|
||||||
|
return [rotAReward, rotBReward];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sendCodaFinishedMessage = async (
|
||||||
|
inventory: TInventoryDatabaseDocument,
|
||||||
|
fp: bigint = generateRewardSeed(),
|
||||||
|
name: string = "ZEKE_BEATWOMAN_TM.1999",
|
||||||
|
killed: boolean = true
|
||||||
|
): Promise<void> => {
|
||||||
|
const att: string[] = [];
|
||||||
|
|
||||||
|
// First vanquish/convert gives a sigil
|
||||||
|
const sigil = killed
|
||||||
|
? "/Lotus/Upgrades/Skins/Sigils/InfLichVanquishedSigil"
|
||||||
|
: "/Lotus/Upgrades/Skins/Sigils/InfLichConvertedSigil";
|
||||||
|
if (!inventory.WeaponSkins.find(x => x.ItemType == sigil)) {
|
||||||
|
att.push(toStoreItem(sigil));
|
||||||
|
}
|
||||||
|
|
||||||
|
const [rotAReward, rotBReward] = getInfestedLichItemRewards(fp);
|
||||||
|
att.push(fromStoreItem(rotAReward));
|
||||||
|
att.push(fromStoreItem(rotBReward));
|
||||||
|
|
||||||
|
let countedAtt: ITypeCount[] | undefined;
|
||||||
|
if (killed) {
|
||||||
|
countedAtt = [
|
||||||
|
{
|
||||||
|
ItemType: "/Lotus/Types/Items/MiscItems/CodaWeaponBucks",
|
||||||
|
ItemCount: getKillTokenRewardCount(fp)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
await createMessage(inventory.accountOwnerId, [
|
||||||
|
{
|
||||||
|
sndr: "/Lotus/Language/Bosses/Ordis",
|
||||||
|
msg: "/Lotus/Language/Inbox/VanquishBandMsgBody",
|
||||||
|
arg: [
|
||||||
|
{
|
||||||
|
Key: "LICH_NAME",
|
||||||
|
Tag: name
|
||||||
|
}
|
||||||
|
],
|
||||||
|
att: att,
|
||||||
|
countedAtt: countedAtt,
|
||||||
|
sub: "/Lotus/Language/Inbox/VanquishBandMsgTitle",
|
||||||
|
icon: "/Lotus/Interface/Icons/Npcs/Ordis.png",
|
||||||
|
highPriority: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
@ -31,7 +31,7 @@ export interface IFingerprintStat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFingerprint => {
|
export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFingerprint => {
|
||||||
const challenge = getRandomElement(meta.availableChallenges!);
|
const challenge = getRandomElement(meta.availableChallenges!)!;
|
||||||
const fingerprintChallenge: IRivenChallenge = {
|
const fingerprintChallenge: IRivenChallenge = {
|
||||||
Type: challenge.fullName,
|
Type: challenge.fullName,
|
||||||
Progress: 0,
|
Progress: 0,
|
||||||
@ -54,11 +54,11 @@ export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFinger
|
|||||||
|
|
||||||
export const createUnveiledRivenFingerprint = (meta: IUpgrade): IUnveiledRivenFingerprint => {
|
export const createUnveiledRivenFingerprint = (meta: IUpgrade): IUnveiledRivenFingerprint => {
|
||||||
const fingerprint: IUnveiledRivenFingerprint = {
|
const fingerprint: IUnveiledRivenFingerprint = {
|
||||||
compat: getRandomElement(meta.compatibleItems!),
|
compat: getRandomElement(meta.compatibleItems!)!,
|
||||||
lim: 0,
|
lim: 0,
|
||||||
lvl: 0,
|
lvl: 0,
|
||||||
lvlReq: getRandomInt(8, 16),
|
lvlReq: getRandomInt(8, 16),
|
||||||
pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"]),
|
pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"])!,
|
||||||
buffs: [],
|
buffs: [],
|
||||||
curses: []
|
curses: []
|
||||||
};
|
};
|
||||||
@ -81,7 +81,7 @@ export const randomiseRivenStats = (meta: IUpgrade, fingerprint: IUnveiledRivenF
|
|||||||
if (Math.random() < 0.5) {
|
if (Math.random() < 0.5) {
|
||||||
const entry = getRandomElement(
|
const entry = getRandomElement(
|
||||||
meta.upgradeEntries!.filter(x => x.canBeCurse && !fingerprint.buffs.find(y => y.Tag == x.tag))
|
meta.upgradeEntries!.filter(x => x.canBeCurse && !fingerprint.buffs.find(y => y.Tag == x.tag))
|
||||||
);
|
)!;
|
||||||
fingerprint.curses.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) });
|
fingerprint.curses.push({ Tag: entry.tag, Value: Math.trunc(Math.random() * 0x40000000) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,130 +0,0 @@
|
|||||||
export const missionTags = [
|
|
||||||
"MT_ASSASSINATION",
|
|
||||||
"MT_EXTERMINATION",
|
|
||||||
"MT_SURVIVAL",
|
|
||||||
"MT_RESCUE",
|
|
||||||
"MT_SABOTAGE",
|
|
||||||
"MT_CAPTURE",
|
|
||||||
"MT_COUNTER_INTEL",
|
|
||||||
"MT_INTEL",
|
|
||||||
"MT_DEFENSE",
|
|
||||||
"MT_MOBILE_DEFENSE",
|
|
||||||
"MT_PVP",
|
|
||||||
"MT_MASTERY",
|
|
||||||
"MT_RECOVERY",
|
|
||||||
"MT_TERRITORY",
|
|
||||||
"MT_RETRIEVAL",
|
|
||||||
"MT_HIVE",
|
|
||||||
"MT_SALVAGE",
|
|
||||||
"MT_EXCAVATE",
|
|
||||||
"MT_RAID",
|
|
||||||
"MT_PURGE",
|
|
||||||
"MT_GENERIC",
|
|
||||||
"MT_PURIFY",
|
|
||||||
"MT_ARENA",
|
|
||||||
"MT_JUNCTION",
|
|
||||||
"MT_PURSUIT",
|
|
||||||
"MT_RACE",
|
|
||||||
"MT_ASSAULT",
|
|
||||||
"MT_EVACUATION",
|
|
||||||
"MT_LANDSCAPE",
|
|
||||||
"MT_RESOURCE_THEFT",
|
|
||||||
"MT_ENDLESS_EXTERMINATION",
|
|
||||||
"MT_ENDLESS_DUVIRI",
|
|
||||||
"MT_RAILJACK",
|
|
||||||
"MT_ARTIFACT",
|
|
||||||
"MT_CORRUPTION",
|
|
||||||
"MT_VOID_CASCADE",
|
|
||||||
"MT_ARMAGEDDON",
|
|
||||||
"MT_VAULTS",
|
|
||||||
"MT_ALCHEMY",
|
|
||||||
"MT_ASCENSION",
|
|
||||||
"MT_ENDLESS_CAPTURE",
|
|
||||||
"MT_OFFERING",
|
|
||||||
"MT_PVPVE"
|
|
||||||
];
|
|
||||||
|
|
||||||
export const sortieBosses = [
|
|
||||||
"SORTIE_BOSS_HYENA",
|
|
||||||
"SORTIE_BOSS_KELA",
|
|
||||||
"SORTIE_BOSS_VOR",
|
|
||||||
"SORTIE_BOSS_RUK",
|
|
||||||
"SORTIE_BOSS_HEK",
|
|
||||||
"SORTIE_BOSS_KRIL",
|
|
||||||
"SORTIE_BOSS_TYL",
|
|
||||||
"SORTIE_BOSS_JACKAL",
|
|
||||||
"SORTIE_BOSS_ALAD",
|
|
||||||
"SORTIE_BOSS_AMBULAS",
|
|
||||||
"SORTIE_BOSS_NEF",
|
|
||||||
"SORTIE_BOSS_RAPTOR",
|
|
||||||
"SORTIE_BOSS_PHORID",
|
|
||||||
"SORTIE_BOSS_LEPHANTIS",
|
|
||||||
"SORTIE_BOSS_INFALAD",
|
|
||||||
"SORTIE_BOSS_CORRUPTED_VOR"
|
|
||||||
];
|
|
||||||
|
|
||||||
export const sortieBossToFaction: Record<string, string> = {
|
|
||||||
SORTIE_BOSS_HYENA: "FC_CORPUS",
|
|
||||||
SORTIE_BOSS_KELA: "FC_GRINEER",
|
|
||||||
SORTIE_BOSS_VOR: "FC_GRINEER",
|
|
||||||
SORTIE_BOSS_RUK: "FC_GRINEER",
|
|
||||||
SORTIE_BOSS_HEK: "FC_GRINEER",
|
|
||||||
SORTIE_BOSS_KRIL: "FC_GRINEER",
|
|
||||||
SORTIE_BOSS_TYL: "FC_GRINEER",
|
|
||||||
SORTIE_BOSS_JACKAL: "FC_CORPUS",
|
|
||||||
SORTIE_BOSS_ALAD: "FC_CORPUS",
|
|
||||||
SORTIE_BOSS_AMBULAS: "FC_CORPUS",
|
|
||||||
SORTIE_BOSS_NEF: "FC_CORPUS",
|
|
||||||
SORTIE_BOSS_RAPTOR: "FC_CORPUS",
|
|
||||||
SORTIE_BOSS_PHORID: "FC_INFESTATION",
|
|
||||||
SORTIE_BOSS_LEPHANTIS: "FC_INFESTATION",
|
|
||||||
SORTIE_BOSS_INFALAD: "FC_INFESTATION",
|
|
||||||
SORTIE_BOSS_CORRUPTED_VOR: "FC_CORRUPTED"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sortieFactionToSystemIndexes: Record<string, number[]> = {
|
|
||||||
FC_GRINEER: [0, 2, 3, 5, 6, 9, 11, 18],
|
|
||||||
FC_CORPUS: [1, 4, 7, 8, 12, 15],
|
|
||||||
FC_INFESTATION: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15],
|
|
||||||
FC_CORRUPTED: [14]
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sortieFactionToFactionIndexes: Record<string, number[]> = {
|
|
||||||
FC_GRINEER: [0],
|
|
||||||
FC_CORPUS: [1],
|
|
||||||
FC_INFESTATION: [0, 1, 2],
|
|
||||||
FC_CORRUPTED: [3]
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sortieBossNode: Record<string, string> = {
|
|
||||||
SORTIE_BOSS_HYENA: "SolNode127",
|
|
||||||
SORTIE_BOSS_KELA: "SolNode193",
|
|
||||||
SORTIE_BOSS_VOR: "SolNode108",
|
|
||||||
SORTIE_BOSS_RUK: "SolNode32",
|
|
||||||
SORTIE_BOSS_HEK: "SolNode24",
|
|
||||||
SORTIE_BOSS_KRIL: "SolNode99",
|
|
||||||
SORTIE_BOSS_TYL: "SolNode105",
|
|
||||||
SORTIE_BOSS_JACKAL: "SolNode104",
|
|
||||||
SORTIE_BOSS_ALAD: "SolNode53",
|
|
||||||
SORTIE_BOSS_AMBULAS: "SolNode51",
|
|
||||||
SORTIE_BOSS_NEF: "SettlementNode20",
|
|
||||||
SORTIE_BOSS_RAPTOR: "SolNode210",
|
|
||||||
SORTIE_BOSS_LEPHANTIS: "SolNode712",
|
|
||||||
SORTIE_BOSS_INFALAD: "SolNode705"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EPOCH = 1734307200 * 1000; // Monday, Dec 16, 2024 @ 00:00 UTC+0; should logically be winter in 1999 iteration 0
|
|
||||||
|
|
||||||
export const getSortieTime = (day: number): number => {
|
|
||||||
const dayStart = EPOCH + day * 86400000;
|
|
||||||
const date = new Date(dayStart);
|
|
||||||
date.setUTCHours(12);
|
|
||||||
const isDst = new Intl.DateTimeFormat("en-US", {
|
|
||||||
timeZone: "America/Toronto",
|
|
||||||
timeZoneName: "short"
|
|
||||||
})
|
|
||||||
.formatToParts(date)
|
|
||||||
.find(part => part.type === "timeZoneName")!
|
|
||||||
.value.includes("DT");
|
|
||||||
return dayStart + (isDst ? 16 : 17) * 3600000;
|
|
||||||
};
|
|
@ -23,6 +23,7 @@ const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
|
|||||||
Type: String,
|
Type: String,
|
||||||
Pos: [Number],
|
Pos: [Number],
|
||||||
Rot: [Number],
|
Rot: [Number],
|
||||||
|
Scale: Number,
|
||||||
Name: String,
|
Name: String,
|
||||||
Sockets: Number,
|
Sockets: Number,
|
||||||
RegularCredits: Number,
|
RegularCredits: Number,
|
||||||
@ -43,6 +44,7 @@ const dojoLeaderboardEntrySchema = new Schema<IDojoLeaderboardEntry>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
|
const dojoComponentSchema = new Schema<IDojoComponentDatabase>({
|
||||||
|
SortId: Schema.Types.ObjectId,
|
||||||
pf: { type: String, required: true },
|
pf: { type: String, required: true },
|
||||||
ppf: String,
|
ppf: String,
|
||||||
pi: Schema.Types.ObjectId,
|
pi: Schema.Types.ObjectId,
|
||||||
@ -190,6 +192,7 @@ const guildSchema = new Schema<IGuildDatabase>(
|
|||||||
VaultMiscItems: { type: [typeCountSchema], default: undefined },
|
VaultMiscItems: { type: [typeCountSchema], default: undefined },
|
||||||
VaultShipDecorations: { type: [typeCountSchema], default: undefined },
|
VaultShipDecorations: { type: [typeCountSchema], default: undefined },
|
||||||
VaultFusionTreasures: { type: [fusionTreasuresSchema], default: undefined },
|
VaultFusionTreasures: { type: [fusionTreasuresSchema], default: undefined },
|
||||||
|
VaultDecoRecipes: { type: [typeCountSchema], default: undefined },
|
||||||
TechProjects: { type: [techProjectSchema], default: undefined },
|
TechProjects: { type: [techProjectSchema], default: undefined },
|
||||||
ActiveDojoColorResearch: { type: String, default: "" },
|
ActiveDojoColorResearch: { type: String, default: "" },
|
||||||
Class: { type: Number, default: 0 },
|
Class: { type: Number, default: 0 },
|
||||||
|
@ -5,7 +5,7 @@ import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
|||||||
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
|
||||||
export interface IMessageClient
|
export interface IMessageClient
|
||||||
extends Omit<IMessageDatabase, "_id" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly"> {
|
extends Omit<IMessageDatabase, "_id" | "date" | "startDate" | "endDate" | "ownerId" | "attVisualOnly" | "expiry"> {
|
||||||
_id?: IOid;
|
_id?: IOid;
|
||||||
date: IMongoDate;
|
date: IMongoDate;
|
||||||
startDate?: IMongoDate;
|
startDate?: IMongoDate;
|
||||||
@ -16,6 +16,8 @@ export interface IMessageClient
|
|||||||
export interface IMessageDatabase extends IMessage {
|
export interface IMessageDatabase extends IMessage {
|
||||||
ownerId: Types.ObjectId;
|
ownerId: Types.ObjectId;
|
||||||
date: Date; //created at
|
date: Date; //created at
|
||||||
|
attVisualOnly?: boolean;
|
||||||
|
expiry?: Date;
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +32,6 @@ export interface IMessage {
|
|||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
att?: string[];
|
att?: string[];
|
||||||
countedAtt?: ITypeCount[];
|
countedAtt?: ITypeCount[];
|
||||||
attVisualOnly?: boolean;
|
|
||||||
transmission?: string;
|
transmission?: string;
|
||||||
arg?: Arg[];
|
arg?: Arg[];
|
||||||
gifts?: IGift[];
|
gifts?: IGift[];
|
||||||
@ -137,14 +138,14 @@ messageSchema.virtual("messageId").get(function (this: IMessageDatabase) {
|
|||||||
messageSchema.set("toJSON", {
|
messageSchema.set("toJSON", {
|
||||||
virtuals: true,
|
virtuals: true,
|
||||||
transform(_document, returnedObject) {
|
transform(_document, returnedObject) {
|
||||||
delete returnedObject.ownerId;
|
|
||||||
|
|
||||||
const messageDatabase = returnedObject as IMessageDatabase;
|
const messageDatabase = returnedObject as IMessageDatabase;
|
||||||
const messageClient = returnedObject as IMessageClient;
|
const messageClient = returnedObject as IMessageClient;
|
||||||
|
|
||||||
delete returnedObject._id;
|
delete returnedObject._id;
|
||||||
delete returnedObject.__v;
|
delete returnedObject.__v;
|
||||||
|
delete returnedObject.ownerId;
|
||||||
delete returnedObject.attVisualOnly;
|
delete returnedObject.attVisualOnly;
|
||||||
|
delete returnedObject.expiry;
|
||||||
|
|
||||||
messageClient.date = toMongoDate(messageDatabase.date);
|
messageClient.date = toMongoDate(messageDatabase.date);
|
||||||
|
|
||||||
@ -157,5 +158,6 @@ messageSchema.set("toJSON", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
messageSchema.index({ ownerId: 1 });
|
messageSchema.index({ ownerId: 1 });
|
||||||
|
messageSchema.index({ expiry: 1 }, { expireAfterSeconds: 0 });
|
||||||
|
|
||||||
export const Inbox = model<IMessageDatabase>("Inbox", messageSchema, "inbox");
|
export const Inbox = model<IMessageDatabase>("Inbox", messageSchema, "inbox");
|
||||||
|
@ -39,10 +39,9 @@ import {
|
|||||||
ILoreFragmentScan,
|
ILoreFragmentScan,
|
||||||
IEvolutionProgress,
|
IEvolutionProgress,
|
||||||
IEndlessXpProgress,
|
IEndlessXpProgress,
|
||||||
ICrewShipPortGuns,
|
|
||||||
ICrewShipCustomization,
|
ICrewShipCustomization,
|
||||||
ICrewShipWeapon,
|
ICrewShipWeapon,
|
||||||
ICrewShipPilotWeapon,
|
ICrewShipWeaponEmplacements,
|
||||||
IShipExterior,
|
IShipExterior,
|
||||||
IHelminthFoodRecord,
|
IHelminthFoodRecord,
|
||||||
ICrewShipMembersDatabase,
|
ICrewShipMembersDatabase,
|
||||||
@ -84,7 +83,21 @@ import {
|
|||||||
IInfNode,
|
IInfNode,
|
||||||
IDiscoveredMarker,
|
IDiscoveredMarker,
|
||||||
IWeeklyMission,
|
IWeeklyMission,
|
||||||
ILockedWeaponGroupDatabase
|
ILockedWeaponGroupDatabase,
|
||||||
|
IPersonalTechProjectDatabase,
|
||||||
|
IPersonalTechProjectClient,
|
||||||
|
ILastSortieRewardDatabase,
|
||||||
|
ILastSortieRewardClient,
|
||||||
|
ICrewMemberSkill,
|
||||||
|
ICrewMemberSkillEfficiency,
|
||||||
|
ICrewMemberDatabase,
|
||||||
|
ICrewMemberClient,
|
||||||
|
ISortieRewardAttenuation,
|
||||||
|
IInvasionProgressDatabase,
|
||||||
|
IInvasionProgressClient,
|
||||||
|
IAccolades,
|
||||||
|
IHubNpcCustomization,
|
||||||
|
ILotusCustomization
|
||||||
} from "../../types/inventoryTypes/inventoryTypes";
|
} from "../../types/inventoryTypes/inventoryTypes";
|
||||||
import { IOid } from "../../types/commonTypes";
|
import { IOid } from "../../types/commonTypes";
|
||||||
import {
|
import {
|
||||||
@ -98,7 +111,7 @@ import {
|
|||||||
IEquipmentClient
|
IEquipmentClient
|
||||||
} from "@/src/types/inventoryTypes/commonInventoryTypes";
|
} from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
import { EquipmentSelectionSchema } from "./loadoutModel";
|
import { EquipmentSelectionSchema, oidSchema } from "./loadoutModel";
|
||||||
|
|
||||||
export const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
|
export const typeCountSchema = new Schema<ITypeCount>({ ItemType: String, ItemCount: Number }, { _id: false });
|
||||||
|
|
||||||
@ -290,6 +303,55 @@ upgradeSchema.set("toJSON", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const crewMemberSkillSchema = new Schema<ICrewMemberSkill>(
|
||||||
|
{
|
||||||
|
Assigned: Number
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const crewMemberSkillEfficiencySchema = new Schema<ICrewMemberSkillEfficiency>(
|
||||||
|
{
|
||||||
|
PILOTING: crewMemberSkillSchema,
|
||||||
|
GUNNERY: crewMemberSkillSchema,
|
||||||
|
ENGINEERING: crewMemberSkillSchema,
|
||||||
|
COMBAT: crewMemberSkillSchema,
|
||||||
|
SURVIVABILITY: crewMemberSkillSchema
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const crewMemberSchema = new Schema<ICrewMemberDatabase>(
|
||||||
|
{
|
||||||
|
ItemType: { type: String, required: true },
|
||||||
|
NemesisFingerprint: { type: BigInt, default: 0n },
|
||||||
|
Seed: { type: BigInt, default: 0n },
|
||||||
|
AssignedRole: Number,
|
||||||
|
SkillEfficiency: crewMemberSkillEfficiencySchema,
|
||||||
|
WeaponConfigIdx: Number,
|
||||||
|
WeaponId: { type: Schema.Types.ObjectId, default: "000000000000000000000000" },
|
||||||
|
XP: { type: Number, default: 0 },
|
||||||
|
PowersuitType: { type: String, required: true },
|
||||||
|
Configs: [ItemConfigSchema],
|
||||||
|
SecondInCommand: { type: Boolean, default: false }
|
||||||
|
},
|
||||||
|
{ id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
crewMemberSchema.set("toJSON", {
|
||||||
|
virtuals: true,
|
||||||
|
transform(_doc, obj) {
|
||||||
|
const db = obj as ICrewMemberDatabase;
|
||||||
|
const client = obj as ICrewMemberClient;
|
||||||
|
|
||||||
|
client.WeaponId = toOid(db.WeaponId);
|
||||||
|
client.ItemId = toOid(db._id);
|
||||||
|
|
||||||
|
delete obj._id;
|
||||||
|
delete obj.__v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const slotsBinSchema = new Schema<ISlots>(
|
const slotsBinSchema = new Schema<ISlots>(
|
||||||
{
|
{
|
||||||
Slots: Number,
|
Slots: Number,
|
||||||
@ -329,8 +391,8 @@ MailboxSchema.set("toJSON", {
|
|||||||
|
|
||||||
const DuviriInfoSchema = new Schema<IDuviriInfo>(
|
const DuviriInfoSchema = new Schema<IDuviriInfo>(
|
||||||
{
|
{
|
||||||
Seed: Number,
|
Seed: { type: BigInt, required: true },
|
||||||
NumCompletions: { type: Number, default: 0 }
|
NumCompletions: { type: Number, required: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: false,
|
_id: false,
|
||||||
@ -498,7 +560,39 @@ const seasonChallengeHistorySchema = new Schema<ISeasonChallenge>(
|
|||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
//TODO: check whether this is complete
|
const personalTechProjectSchema = new Schema<IPersonalTechProjectDatabase>({
|
||||||
|
State: Number,
|
||||||
|
ReqCredits: Number,
|
||||||
|
ItemType: String,
|
||||||
|
ProductCategory: String,
|
||||||
|
CategoryItemId: Schema.Types.ObjectId,
|
||||||
|
ReqItems: { type: [typeCountSchema], default: undefined },
|
||||||
|
HasContributions: Boolean,
|
||||||
|
CompletionDate: Date
|
||||||
|
});
|
||||||
|
|
||||||
|
personalTechProjectSchema.virtual("ItemId").get(function () {
|
||||||
|
return { $oid: this._id.toString() };
|
||||||
|
});
|
||||||
|
|
||||||
|
personalTechProjectSchema.set("toJSON", {
|
||||||
|
virtuals: true,
|
||||||
|
transform(_doc, ret, _options) {
|
||||||
|
delete ret._id;
|
||||||
|
delete ret.__v;
|
||||||
|
|
||||||
|
const db = ret as IPersonalTechProjectDatabase;
|
||||||
|
const client = ret as IPersonalTechProjectClient;
|
||||||
|
|
||||||
|
if (db.CategoryItemId) {
|
||||||
|
client.CategoryItemId = toOid(db.CategoryItemId);
|
||||||
|
}
|
||||||
|
if (db.CompletionDate) {
|
||||||
|
client.CompletionDate = toMongoDate(db.CompletionDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const playerSkillsSchema = new Schema<IPlayerSkills>(
|
const playerSkillsSchema = new Schema<IPlayerSkills>(
|
||||||
{
|
{
|
||||||
LPP_SPACE: { type: Number, default: 0 },
|
LPP_SPACE: { type: Number, default: 0 },
|
||||||
@ -595,6 +689,27 @@ questKeysSchema.set("toJSON", {
|
|||||||
|
|
||||||
export const fusionTreasuresSchema = new Schema<IFusionTreasure>().add(typeCountSchema).add({ Sockets: Number });
|
export const fusionTreasuresSchema = new Schema<IFusionTreasure>().add(typeCountSchema).add({ Sockets: Number });
|
||||||
|
|
||||||
|
const invasionProgressSchema = new Schema<IInvasionProgressDatabase>(
|
||||||
|
{
|
||||||
|
invasionId: Schema.Types.ObjectId,
|
||||||
|
Delta: Number,
|
||||||
|
AttackerScore: Number,
|
||||||
|
DefenderScore: Number
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
invasionProgressSchema.set("toJSON", {
|
||||||
|
transform(_doc, obj) {
|
||||||
|
const db = obj as IInvasionProgressDatabase;
|
||||||
|
const client = obj as IInvasionProgressClient;
|
||||||
|
|
||||||
|
client._id = toOid(db.invasionId);
|
||||||
|
delete obj.invasionId;
|
||||||
|
delete obj.__v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const spectreLoadoutsSchema = new Schema<ISpectreLoadout>(
|
const spectreLoadoutsSchema = new Schema<ISpectreLoadout>(
|
||||||
{
|
{
|
||||||
ItemType: String,
|
ItemType: String,
|
||||||
@ -612,6 +727,7 @@ const spectreLoadoutsSchema = new Schema<ISpectreLoadout>(
|
|||||||
const weaponSkinsSchema = new Schema<IWeaponSkinDatabase>(
|
const weaponSkinsSchema = new Schema<IWeaponSkinDatabase>(
|
||||||
{
|
{
|
||||||
ItemType: String,
|
ItemType: String,
|
||||||
|
Favorite: Boolean,
|
||||||
IsNew: Boolean
|
IsNew: Boolean
|
||||||
},
|
},
|
||||||
{ id: false }
|
{ id: false }
|
||||||
@ -665,6 +781,26 @@ const loreFragmentScansSchema = new Schema<ILoreFragmentScan>(
|
|||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// const lotusCustomizationSchema = new Schema<ILotusCustomization>().add(ItemConfigSchema).add({
|
||||||
|
// Persona: String
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Laxer schema for cleanupInventory
|
||||||
|
const lotusCustomizationSchema = new Schema<ILotusCustomization>(
|
||||||
|
{
|
||||||
|
Skins: [String],
|
||||||
|
pricol: colorSchema,
|
||||||
|
attcol: Schema.Types.Mixed,
|
||||||
|
sigcol: Schema.Types.Mixed,
|
||||||
|
eyecol: Schema.Types.Mixed,
|
||||||
|
facial: Schema.Types.Mixed,
|
||||||
|
cloth: Schema.Types.Mixed,
|
||||||
|
syancol: Schema.Types.Mixed,
|
||||||
|
Persona: String
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
const evolutionProgressSchema = new Schema<IEvolutionProgress>(
|
const evolutionProgressSchema = new Schema<IEvolutionProgress>(
|
||||||
{
|
{
|
||||||
Progress: Number,
|
Progress: Number,
|
||||||
@ -682,25 +818,23 @@ const endlessXpProgressSchema = new Schema<IEndlessXpProgress>(
|
|||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
const crewShipPilotWeaponSchema = new Schema<ICrewShipPilotWeapon>(
|
const crewShipWeaponEmplacementsSchema = new Schema<ICrewShipWeaponEmplacements>(
|
||||||
{
|
{
|
||||||
PRIMARY_A: EquipmentSelectionSchema,
|
PRIMARY_A: EquipmentSelectionSchema,
|
||||||
SECONDARY_A: EquipmentSelectionSchema
|
PRIMARY_B: EquipmentSelectionSchema,
|
||||||
},
|
SECONDARY_A: EquipmentSelectionSchema,
|
||||||
{ _id: false }
|
SECONDARY_B: EquipmentSelectionSchema
|
||||||
);
|
|
||||||
|
|
||||||
const crewShipPortGunsSchema = new Schema<ICrewShipPortGuns>(
|
|
||||||
{
|
|
||||||
PRIMARY_A: EquipmentSelectionSchema
|
|
||||||
},
|
},
|
||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
const crewShipWeaponSchema = new Schema<ICrewShipWeapon>(
|
const crewShipWeaponSchema = new Schema<ICrewShipWeapon>(
|
||||||
{
|
{
|
||||||
PILOT: crewShipPilotWeaponSchema,
|
PILOT: crewShipWeaponEmplacementsSchema,
|
||||||
PORT_GUNS: crewShipPortGunsSchema
|
PORT_GUNS: crewShipWeaponEmplacementsSchema,
|
||||||
|
STARBOARD_GUNS: crewShipWeaponEmplacementsSchema,
|
||||||
|
ARTILLERY: crewShipWeaponEmplacementsSchema,
|
||||||
|
SCANNER: crewShipWeaponEmplacementsSchema
|
||||||
},
|
},
|
||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
@ -724,7 +858,7 @@ const crewShipCustomizationSchema = new Schema<ICrewShipCustomization>(
|
|||||||
const crewShipMemberSchema = new Schema<ICrewShipMemberDatabase>(
|
const crewShipMemberSchema = new Schema<ICrewShipMemberDatabase>(
|
||||||
{
|
{
|
||||||
ItemId: { type: Schema.Types.ObjectId, required: false },
|
ItemId: { type: Schema.Types.ObjectId, required: false },
|
||||||
NemesisFingerprint: { type: Number, required: false }
|
NemesisFingerprint: { type: BigInt, required: false }
|
||||||
},
|
},
|
||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
@ -773,7 +907,7 @@ const dialogueSchema = new Schema<IDialogueDatabase>(
|
|||||||
AvailableGiftDate: Date,
|
AvailableGiftDate: Date,
|
||||||
RankUpExpiry: Date,
|
RankUpExpiry: Date,
|
||||||
BountyChemExpiry: Date,
|
BountyChemExpiry: Date,
|
||||||
//QueuedDialogues: ???
|
QueuedDialogues: { type: [String], default: [] },
|
||||||
Gifts: { type: [dialogueGiftSchema], default: [] },
|
Gifts: { type: [dialogueGiftSchema], default: [] },
|
||||||
Booleans: { type: [String], default: [] },
|
Booleans: { type: [String], default: [] },
|
||||||
Completed: { type: [completedDialogueSchema], default: [] },
|
Completed: { type: [completedDialogueSchema], default: [] },
|
||||||
@ -796,7 +930,8 @@ dialogueSchema.set("toJSON", {
|
|||||||
|
|
||||||
const dialogueHistorySchema = new Schema<IDialogueHistoryDatabase>(
|
const dialogueHistorySchema = new Schema<IDialogueHistoryDatabase>(
|
||||||
{
|
{
|
||||||
YearIteration: { type: Number, required: true },
|
YearIteration: Number,
|
||||||
|
Resets: Number,
|
||||||
Dialogues: { type: [dialogueSchema], required: false }
|
Dialogues: { type: [dialogueSchema], required: false }
|
||||||
},
|
},
|
||||||
{ _id: false }
|
{ _id: false }
|
||||||
@ -879,6 +1014,7 @@ const EquipmentSchema = new Schema<IEquipmentDatabase>(
|
|||||||
RailjackImage: FlavourItemSchema,
|
RailjackImage: FlavourItemSchema,
|
||||||
CrewMembers: crewShipMembersSchema,
|
CrewMembers: crewShipMembersSchema,
|
||||||
Details: detailsSchema,
|
Details: detailsSchema,
|
||||||
|
Favorite: Boolean,
|
||||||
IsNew: Boolean
|
IsNew: Boolean
|
||||||
},
|
},
|
||||||
{ id: false }
|
{ id: false }
|
||||||
@ -919,6 +1055,8 @@ const pendingRecipeSchema = new Schema<IPendingRecipeDatabase>(
|
|||||||
{
|
{
|
||||||
ItemType: String,
|
ItemType: String,
|
||||||
CompletionDate: Date,
|
CompletionDate: Date,
|
||||||
|
TargetItemId: String,
|
||||||
|
TargetFingerprint: String,
|
||||||
LongGuns: { type: [EquipmentSchema], default: undefined },
|
LongGuns: { type: [EquipmentSchema], default: undefined },
|
||||||
Pistols: { type: [EquipmentSchema], default: undefined },
|
Pistols: { type: [EquipmentSchema], default: undefined },
|
||||||
Melee: { type: [EquipmentSchema], default: undefined },
|
Melee: { type: [EquipmentSchema], default: undefined },
|
||||||
@ -946,6 +1084,13 @@ pendingRecipeSchema.set("toJSON", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const accoladesSchema = new Schema<IAccolades>(
|
||||||
|
{
|
||||||
|
Heirloom: Boolean
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
|
const infestedFoundrySchema = new Schema<IInfestedFoundryDatabase>(
|
||||||
{
|
{
|
||||||
Name: String,
|
Name: String,
|
||||||
@ -1003,15 +1148,15 @@ const CustomMarkersSchema = new Schema<ICustomMarkers>(
|
|||||||
const calenderProgressSchema = new Schema<ICalendarProgress>(
|
const calenderProgressSchema = new Schema<ICalendarProgress>(
|
||||||
{
|
{
|
||||||
Version: { type: Number, default: 19 },
|
Version: { type: Number, default: 19 },
|
||||||
Iteration: { type: Number, default: 2 },
|
Iteration: { type: Number, required: true },
|
||||||
YearProgress: {
|
YearProgress: {
|
||||||
Upgrades: { type: [] }
|
Upgrades: { type: [String], default: [] }
|
||||||
},
|
},
|
||||||
SeasonProgress: {
|
SeasonProgress: {
|
||||||
SeasonType: String,
|
SeasonType: { type: String, required: true },
|
||||||
LastCompletedDayIdx: { type: Number, default: -1 },
|
LastCompletedDayIdx: { type: Number, default: 0 },
|
||||||
LastCompletedChallengeDayIdx: { type: Number, default: -1 },
|
LastCompletedChallengeDayIdx: { type: Number, default: 0 },
|
||||||
ActivatedChallenges: []
|
ActivatedChallenges: { type: [String], default: [] }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ _id: false }
|
{ _id: false }
|
||||||
@ -1133,11 +1278,11 @@ const nemesisSchema = new Schema<INemesisDatabase>(
|
|||||||
PrevOwners: Number,
|
PrevOwners: Number,
|
||||||
SecondInCommand: Boolean,
|
SecondInCommand: Boolean,
|
||||||
Weakened: Boolean,
|
Weakened: Boolean,
|
||||||
InfNodes: [infNodeSchema],
|
InfNodes: { type: [infNodeSchema], default: undefined },
|
||||||
HenchmenKilled: Number,
|
HenchmenKilled: Number,
|
||||||
HintProgress: Number,
|
HintProgress: Number,
|
||||||
Hints: [Number],
|
Hints: { type: [Number], default: undefined },
|
||||||
GuessHistory: [Number],
|
GuessHistory: { type: [Number], default: undefined },
|
||||||
MissionCount: Number,
|
MissionCount: Number,
|
||||||
LastEnc: Number
|
LastEnc: Number
|
||||||
},
|
},
|
||||||
@ -1165,6 +1310,36 @@ const alignmentSchema = new Schema<IAlignment>(
|
|||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const lastSortieRewardSchema = new Schema<ILastSortieRewardDatabase>(
|
||||||
|
{
|
||||||
|
SortieId: Schema.Types.ObjectId,
|
||||||
|
StoreItem: String,
|
||||||
|
Manifest: String
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
lastSortieRewardSchema.set("toJSON", {
|
||||||
|
virtuals: true,
|
||||||
|
transform(_doc, obj) {
|
||||||
|
const db = obj as ILastSortieRewardDatabase;
|
||||||
|
const client = obj as ILastSortieRewardClient;
|
||||||
|
|
||||||
|
client.SortieId = toOid(db.SortieId);
|
||||||
|
|
||||||
|
delete obj._id;
|
||||||
|
delete obj.__v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortieRewardAttenutationSchema = new Schema<ISortieRewardAttenuation>(
|
||||||
|
{
|
||||||
|
Tag: String,
|
||||||
|
Atten: Number
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
const lockedWeaponGroupSchema = new Schema<ILockedWeaponGroupDatabase>(
|
const lockedWeaponGroupSchema = new Schema<ILockedWeaponGroupDatabase>(
|
||||||
{
|
{
|
||||||
s: Schema.Types.ObjectId,
|
s: Schema.Types.ObjectId,
|
||||||
@ -1176,12 +1351,21 @@ const lockedWeaponGroupSchema = new Schema<ILockedWeaponGroupDatabase>(
|
|||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hubNpcCustomizationSchema = new Schema<IHubNpcCustomization>(
|
||||||
|
{
|
||||||
|
Colors: colorSchema,
|
||||||
|
Pattern: String,
|
||||||
|
Tag: String
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||||
{
|
{
|
||||||
accountOwnerId: Schema.Types.ObjectId,
|
accountOwnerId: Schema.Types.ObjectId,
|
||||||
SubscribedToEmails: { type: Number, default: 0 },
|
SubscribedToEmails: { type: Number, default: 0 },
|
||||||
SubscribedToEmailsPersonalized: { type: Number, default: 0 },
|
SubscribedToEmailsPersonalized: { type: Number, default: 0 },
|
||||||
RewardSeed: Number,
|
RewardSeed: BigInt,
|
||||||
|
|
||||||
//Credit
|
//Credit
|
||||||
RegularCredits: { type: Number, default: 0 },
|
RegularCredits: { type: Number, default: 0 },
|
||||||
@ -1215,7 +1399,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
//How many Gift do you have left*(gift spends the trade)
|
//How many Gift do you have left*(gift spends the trade)
|
||||||
GiftsRemaining: { type: Number, default: 8 },
|
GiftsRemaining: { type: Number, default: 8 },
|
||||||
//Curent trade info Giving or Getting items
|
//Curent trade info Giving or Getting items
|
||||||
PendingTrades: [Schema.Types.Mixed],
|
//PendingTrades: [Schema.Types.Mixed],
|
||||||
|
|
||||||
//Syndicate currently being pledged to.
|
//Syndicate currently being pledged to.
|
||||||
SupportedSyndicate: String,
|
SupportedSyndicate: String,
|
||||||
@ -1265,7 +1449,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: [Schema.Types.Mixed],
|
||||||
|
|
||||||
//Item for EquippedGear example:Scaner,LoadoutTechSummon etc
|
//Item for EquippedGear example:Scaner,LoadoutTechSummon etc
|
||||||
Consumables: [typeCountSchema],
|
Consumables: [typeCountSchema],
|
||||||
@ -1302,7 +1486,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
CrewShipSalvagedWeaponSkins: [upgradeSchema],
|
CrewShipSalvagedWeaponSkins: [upgradeSchema],
|
||||||
|
|
||||||
//RailJack Crew
|
//RailJack Crew
|
||||||
CrewMembers: [Schema.Types.Mixed],
|
CrewMembers: [crewMemberSchema],
|
||||||
|
|
||||||
//Complete Mission\Quests
|
//Complete Mission\Quests
|
||||||
Missions: [missionSchema],
|
Missions: [missionSchema],
|
||||||
@ -1311,7 +1495,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
//item like DojoKey or Boss missions key
|
//item like DojoKey or Boss missions key
|
||||||
LevelKeys: [typeCountSchema],
|
LevelKeys: [typeCountSchema],
|
||||||
//Active quests
|
//Active quests
|
||||||
Quests: [Schema.Types.Mixed],
|
//Quests: [Schema.Types.Mixed],
|
||||||
|
|
||||||
//Cosmetics like profile glyphs\Kavasa Prime Kubrow Collar\Game Theme etc
|
//Cosmetics like profile glyphs\Kavasa Prime Kubrow Collar\Game Theme etc
|
||||||
FlavourItems: [FlavourItemSchema],
|
FlavourItems: [FlavourItemSchema],
|
||||||
@ -1323,6 +1507,16 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
//Mastery Rank next availability
|
//Mastery Rank next availability
|
||||||
TrainingDate: { type: Date, default: new Date(0) },
|
TrainingDate: { type: Date, default: new Date(0) },
|
||||||
|
|
||||||
|
//Accolades
|
||||||
|
Staff: Boolean,
|
||||||
|
Founder: Number,
|
||||||
|
Guide: Number,
|
||||||
|
Moderator: Boolean,
|
||||||
|
Partner: Boolean,
|
||||||
|
Accolades: accoladesSchema,
|
||||||
|
//Not an accolade but unlocks an extra chat
|
||||||
|
Counselor: Boolean,
|
||||||
|
|
||||||
//you saw last played Region when you opened the star map
|
//you saw last played Region when you opened the star map
|
||||||
LastRegionPlayed: String,
|
LastRegionPlayed: String,
|
||||||
|
|
||||||
@ -1340,7 +1534,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
TauntHistory: { type: [tauntSchema], default: undefined },
|
TauntHistory: { type: [tauntSchema], default: undefined },
|
||||||
|
|
||||||
//noShow2FA,VisitPrimeVault etc
|
//noShow2FA,VisitPrimeVault etc
|
||||||
WebFlags: Schema.Types.Mixed,
|
//WebFlags: Schema.Types.Mixed,
|
||||||
//Id CompletedAlerts
|
//Id CompletedAlerts
|
||||||
CompletedAlerts: [String],
|
CompletedAlerts: [String],
|
||||||
|
|
||||||
@ -1360,9 +1554,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
//the color your clan requests like Items/Research/DojoColors/DojoColorPlainsB
|
//the color your clan requests like Items/Research/DojoColors/DojoColorPlainsB
|
||||||
ActiveDojoColorResearch: String,
|
ActiveDojoColorResearch: String,
|
||||||
|
|
||||||
SentientSpawnChanceBoosters: Schema.Types.Mixed,
|
//SentientSpawnChanceBoosters: Schema.Types.Mixed,
|
||||||
|
|
||||||
QualifyingInvasions: [Schema.Types.Mixed],
|
QualifyingInvasions: [invasionProgressSchema],
|
||||||
FactionScores: [Number],
|
FactionScores: [Number],
|
||||||
|
|
||||||
// https://warframe.fandom.com/wiki/Specter_(Tenno)
|
// https://warframe.fandom.com/wiki/Specter_(Tenno)
|
||||||
@ -1382,7 +1576,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
|
|
||||||
//https://warframe.fandom.com/wiki/Sortie
|
//https://warframe.fandom.com/wiki/Sortie
|
||||||
CompletedSorties: [String],
|
CompletedSorties: [String],
|
||||||
LastSortieReward: [Schema.Types.Mixed],
|
LastSortieReward: { type: [lastSortieRewardSchema], default: undefined },
|
||||||
|
LastLiteSortieReward: { type: [lastSortieRewardSchema], default: undefined },
|
||||||
|
SortieRewardAttenuation: { type: [sortieRewardAttenutationSchema], default: undefined },
|
||||||
|
|
||||||
// Resource Extractor Drones
|
// Resource Extractor Drones
|
||||||
Drones: [droneSchema],
|
Drones: [droneSchema],
|
||||||
@ -1393,10 +1589,10 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
// open location store like EidolonPlainsDiscoverable or OrbVallisCaveDiscoverable
|
// open location store like EidolonPlainsDiscoverable or OrbVallisCaveDiscoverable
|
||||||
DiscoveredMarkers: [discoveredMarkerSchema],
|
DiscoveredMarkers: [discoveredMarkerSchema],
|
||||||
//Open location mission like "JobId" + "StageCompletions"
|
//Open location mission like "JobId" + "StageCompletions"
|
||||||
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: [Schema.Types.Mixed],
|
||||||
|
|
||||||
//Setting interface Style
|
//Setting interface Style
|
||||||
ThemeStyle: String,
|
ThemeStyle: String,
|
||||||
@ -1414,7 +1610,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
|
|
||||||
//https://warframe.fandom.com/wiki/Heist
|
//https://warframe.fandom.com/wiki/Heist
|
||||||
//ProfitTaker(1-4) Example:"LocationTag": "EudicoHeists", "Jobs":Mission name
|
//ProfitTaker(1-4) Example:"LocationTag": "EudicoHeists", "Jobs":Mission name
|
||||||
CompletedJobChains: [completedJobChainsSchema],
|
CompletedJobChains: { type: [completedJobChainsSchema], default: undefined },
|
||||||
//Night Wave Challenge
|
//Night Wave Challenge
|
||||||
SeasonChallengeHistory: [seasonChallengeHistorySchema],
|
SeasonChallengeHistory: [seasonChallengeHistorySchema],
|
||||||
|
|
||||||
@ -1426,27 +1622,27 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
LibraryActiveDailyTaskInfo: libraryDailyTaskInfoSchema,
|
LibraryActiveDailyTaskInfo: libraryDailyTaskInfoSchema,
|
||||||
|
|
||||||
//https://warframe.fandom.com/wiki/Invasion
|
//https://warframe.fandom.com/wiki/Invasion
|
||||||
InvasionChainProgress: [Schema.Types.Mixed],
|
//InvasionChainProgress: [Schema.Types.Mixed],
|
||||||
|
|
||||||
//CorpusLich or GrineerLich
|
//CorpusLich or GrineerLich
|
||||||
NemesisAbandonedRewards: { type: [String], default: [] },
|
NemesisAbandonedRewards: { type: [String], default: [] },
|
||||||
Nemesis: nemesisSchema,
|
Nemesis: nemesisSchema,
|
||||||
NemesisHistory: [Schema.Types.Mixed],
|
NemesisHistory: { type: [nemesisSchema], default: undefined },
|
||||||
LastNemesisAllySpawnTime: Schema.Types.Mixed,
|
//LastNemesisAllySpawnTime: Schema.Types.Mixed,
|
||||||
|
|
||||||
//TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social)
|
//TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social)
|
||||||
Settings: settingsSchema,
|
Settings: settingsSchema,
|
||||||
|
|
||||||
//Railjack craft
|
//Railjack craft
|
||||||
//https://warframe.fandom.com/wiki/Rising_Tide
|
//https://warframe.fandom.com/wiki/Rising_Tide
|
||||||
PersonalTechProjects: [Schema.Types.Mixed],
|
PersonalTechProjects: { type: [personalTechProjectSchema], default: [] },
|
||||||
|
|
||||||
//Modulars lvl and exp(Railjack|Duviri)
|
//Modulars lvl and exp(Railjack|Duviri)
|
||||||
//https://warframe.fandom.com/wiki/Intrinsics
|
//https://warframe.fandom.com/wiki/Intrinsics
|
||||||
PlayerSkills: { type: playerSkillsSchema, default: {} },
|
PlayerSkills: { type: playerSkillsSchema, default: {} },
|
||||||
|
|
||||||
//TradeBannedUntil data
|
//TradeBannedUntil data
|
||||||
TradeBannedUntil: Schema.Types.Mixed,
|
//TradeBannedUntil: Schema.Types.Mixed,
|
||||||
|
|
||||||
//https://warframe.fandom.com/wiki/Helminth
|
//https://warframe.fandom.com/wiki/Helminth
|
||||||
InfestedFoundry: infestedFoundrySchema,
|
InfestedFoundry: infestedFoundrySchema,
|
||||||
@ -1455,7 +1651,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
|
|
||||||
//Purchase this new permanent skin from the Lotus customization options in Personal Quarters located in your Orbiter.
|
//Purchase this new permanent skin from the Lotus customization options in Personal Quarters located in your Orbiter.
|
||||||
//https://warframe.fandom.com/wiki/Lotus#The_New_War
|
//https://warframe.fandom.com/wiki/Lotus#The_New_War
|
||||||
LotusCustomization: Schema.Types.Mixed,
|
LotusCustomization: { type: lotusCustomizationSchema, default: undefined },
|
||||||
|
|
||||||
//Progress+Rank+ItemType(ZarimanPumpShotgun)
|
//Progress+Rank+ItemType(ZarimanPumpShotgun)
|
||||||
//https://warframe.fandom.com/wiki/Incarnon
|
//https://warframe.fandom.com/wiki/Incarnon
|
||||||
@ -1466,23 +1662,24 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
|
|
||||||
//Unknown and system
|
//Unknown and system
|
||||||
DuviriInfo: DuviriInfoSchema,
|
DuviriInfo: DuviriInfoSchema,
|
||||||
|
LastInventorySync: Schema.Types.ObjectId,
|
||||||
Mailbox: MailboxSchema,
|
Mailbox: MailboxSchema,
|
||||||
HandlerPoints: Number,
|
HandlerPoints: Number,
|
||||||
ChallengesFixVersion: Number,
|
ChallengesFixVersion: { type: Number, default: 6 },
|
||||||
PlayedParkourTutorial: Boolean,
|
PlayedParkourTutorial: Boolean,
|
||||||
ActiveLandscapeTraps: [Schema.Types.Mixed],
|
//ActiveLandscapeTraps: [Schema.Types.Mixed],
|
||||||
RepVotes: [Schema.Types.Mixed],
|
//RepVotes: [Schema.Types.Mixed],
|
||||||
LeagueTickets: [Schema.Types.Mixed],
|
//LeagueTickets: [Schema.Types.Mixed],
|
||||||
HasContributedToDojo: Boolean,
|
HasContributedToDojo: Boolean,
|
||||||
HWIDProtectEnabled: Boolean,
|
HWIDProtectEnabled: Boolean,
|
||||||
LoadOutPresets: { type: Schema.Types.ObjectId, ref: "Loadout" },
|
LoadOutPresets: { type: Schema.Types.ObjectId, ref: "Loadout" },
|
||||||
CurrentLoadOutIds: [Schema.Types.Mixed],
|
CurrentLoadOutIds: [oidSchema],
|
||||||
RandomUpgradesIdentified: Number,
|
RandomUpgradesIdentified: Number,
|
||||||
BountyScore: Number,
|
BountyScore: Number,
|
||||||
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],
|
//UsedDailyDeals: [Schema.Types.Mixed],
|
||||||
CollectibleSeries: { type: [collectibleEntrySchema], default: undefined },
|
CollectibleSeries: { type: [collectibleEntrySchema], default: undefined },
|
||||||
HasResetAccount: { type: Boolean, default: false },
|
HasResetAccount: { type: Boolean, default: false },
|
||||||
|
|
||||||
@ -1491,9 +1688,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
//Like BossAladV,BossCaptainVor come for you on missions % chance
|
//Like BossAladV,BossCaptainVor come for you on missions % chance
|
||||||
DeathMarks: { type: [String], default: [] },
|
DeathMarks: { type: [String], default: [] },
|
||||||
//Zanuka
|
//Zanuka
|
||||||
Harvestable: Boolean,
|
Harvestable: { type: Boolean, default: true },
|
||||||
//Grustag three
|
//Grustag three
|
||||||
DeathSquadable: Boolean,
|
DeathSquadable: { type: Boolean, default: true },
|
||||||
|
|
||||||
EndlessXP: { type: [endlessXpProgressSchema], default: undefined },
|
EndlessXP: { type: [endlessXpProgressSchema], default: undefined },
|
||||||
|
|
||||||
@ -1517,7 +1714,9 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
|||||||
|
|
||||||
// G3 + Zanuka
|
// G3 + Zanuka
|
||||||
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 }
|
||||||
},
|
},
|
||||||
{ timestamps: { createdAt: "Created", updatedAt: false } }
|
{ timestamps: { createdAt: "Created", updatedAt: false } }
|
||||||
);
|
);
|
||||||
@ -1561,6 +1760,9 @@ inventorySchema.set("toJSON", {
|
|||||||
sn: inventoryDatabase.LockedWeaponGroup.sn ? toOid(inventoryDatabase.LockedWeaponGroup.sn) : undefined
|
sn: inventoryDatabase.LockedWeaponGroup.sn ? toOid(inventoryDatabase.LockedWeaponGroup.sn) : undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (inventoryDatabase.LastInventorySync) {
|
||||||
|
inventoryResponse.LastInventorySync = toOid(inventoryDatabase.LastInventorySync);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1581,7 +1783,9 @@ export type InventoryDocumentProps = {
|
|||||||
QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
|
QuestKeys: Types.DocumentArray<IQuestKeyDatabase>;
|
||||||
Drones: Types.DocumentArray<IDroneDatabase>;
|
Drones: Types.DocumentArray<IDroneDatabase>;
|
||||||
CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
CrewShipWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
||||||
CrewShipSalvagedWeaponsSkins: Types.DocumentArray<IUpgradeDatabase>;
|
CrewShipSalvagedWeaponSkins: Types.DocumentArray<IUpgradeDatabase>;
|
||||||
|
PersonalTechProjects: Types.DocumentArray<IPersonalTechProjectDatabase>;
|
||||||
|
CrewMembers: Types.DocumentArray<ICrewMemberDatabase>;
|
||||||
} & { [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
|
||||||
|
@ -3,7 +3,7 @@ import { IEquipmentSelection } from "@/src/types/inventoryTypes/commonInventoryT
|
|||||||
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
|
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
|
||||||
import { Document, Model, Schema, Types, model } from "mongoose";
|
import { Document, Model, Schema, Types, model } from "mongoose";
|
||||||
|
|
||||||
const oidSchema = new Schema<IOid>(
|
export const oidSchema = new Schema<IOid>(
|
||||||
{
|
{
|
||||||
$oid: String
|
$oid: String
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IDatabaseAccountJson } from "@/src/types/loginTypes";
|
import { IDatabaseAccountJson, IIgnore } from "@/src/types/loginTypes";
|
||||||
import { model, Schema, SchemaOptions } from "mongoose";
|
import { model, Schema, SchemaOptions } from "mongoose";
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
@ -37,3 +37,13 @@ databaseAccountSchema.set("toJSON", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const Account = model<IDatabaseAccountJson>("Account", databaseAccountSchema);
|
export const Account = model<IDatabaseAccountJson>("Account", databaseAccountSchema);
|
||||||
|
|
||||||
|
const ignoreSchema = new Schema<IIgnore>({
|
||||||
|
ignorer: Schema.Types.ObjectId,
|
||||||
|
ignoree: Schema.Types.ObjectId
|
||||||
|
});
|
||||||
|
|
||||||
|
ignoreSchema.index({ ignorer: 1 });
|
||||||
|
ignoreSchema.index({ ignorer: 1, ignoree: 1 }, { unique: true });
|
||||||
|
|
||||||
|
export const Ignore = model<IIgnore>("Ignore", ignoreSchema);
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||||
import { colorSchema } from "@/src/models/inventoryModels/inventoryModel";
|
import { colorSchema } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
import { IOrbiter, IPersonalRoomsDatabase, PersonalRoomsModelType } from "@/src/types/personalRoomsTypes";
|
import { IOrbiter, IPersonalRoomsDatabase, PersonalRoomsModelType } from "@/src/types/personalRoomsTypes";
|
||||||
import {
|
import {
|
||||||
IApartment,
|
|
||||||
IFavouriteLoadoutDatabase,
|
IFavouriteLoadoutDatabase,
|
||||||
IGardening,
|
IGardeningDatabase,
|
||||||
IPlacedDecosDatabase,
|
IPlacedDecosDatabase,
|
||||||
IPictureFrameInfo,
|
IPictureFrameInfo,
|
||||||
IRoom,
|
IRoom,
|
||||||
ITailorShopDatabase
|
ITailorShopDatabase,
|
||||||
|
IApartmentDatabase,
|
||||||
|
IPlanterDatabase,
|
||||||
|
IPlantDatabase,
|
||||||
|
IPlantClient
|
||||||
} from "@/src/types/shipTypes";
|
} from "@/src/types/shipTypes";
|
||||||
import { Schema, model } from "mongoose";
|
import { Schema, model } from "mongoose";
|
||||||
|
|
||||||
@ -62,19 +65,64 @@ const roomSchema = new Schema<IRoom>(
|
|||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
const gardeningSchema = new Schema<IGardening>({
|
const favouriteLoadoutSchema = new Schema<IFavouriteLoadoutDatabase>(
|
||||||
Planters: [Schema.Types.Mixed] //TODO: add when implementing gardening
|
|
||||||
});
|
|
||||||
|
|
||||||
const apartmentSchema = new Schema<IApartment>(
|
|
||||||
{
|
{
|
||||||
Rooms: [roomSchema],
|
Tag: String,
|
||||||
FavouriteLoadouts: [Schema.Types.Mixed],
|
LoadoutId: Schema.Types.ObjectId
|
||||||
Gardening: gardeningSchema // TODO: ensure this is correct
|
|
||||||
},
|
},
|
||||||
{ _id: false }
|
{ _id: false }
|
||||||
);
|
);
|
||||||
const apartmentDefault: IApartment = {
|
favouriteLoadoutSchema.set("toJSON", {
|
||||||
|
virtuals: true,
|
||||||
|
transform(_document, returnedObject) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
|
returnedObject.LoadoutId = toOid(returnedObject.LoadoutId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const plantSchema = new Schema<IPlantDatabase>(
|
||||||
|
{
|
||||||
|
PlantType: String,
|
||||||
|
EndTime: Date,
|
||||||
|
PlotIndex: Number
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
plantSchema.set("toJSON", {
|
||||||
|
virtuals: true,
|
||||||
|
transform(_doc, obj) {
|
||||||
|
const client = obj as IPlantClient;
|
||||||
|
const db = obj as IPlantDatabase;
|
||||||
|
|
||||||
|
client.EndTime = toMongoDate(db.EndTime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const planterSchema = new Schema<IPlanterDatabase>(
|
||||||
|
{
|
||||||
|
Name: { type: String, required: true },
|
||||||
|
Plants: { type: [plantSchema], default: [] }
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const gardeningSchema = new Schema<IGardeningDatabase>(
|
||||||
|
{
|
||||||
|
Planters: { type: [planterSchema], default: [] }
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const apartmentSchema = new Schema<IApartmentDatabase>(
|
||||||
|
{
|
||||||
|
Rooms: [roomSchema],
|
||||||
|
FavouriteLoadouts: [favouriteLoadoutSchema],
|
||||||
|
Gardening: gardeningSchema
|
||||||
|
},
|
||||||
|
{ _id: false }
|
||||||
|
);
|
||||||
|
const apartmentDefault: IApartmentDatabase = {
|
||||||
Rooms: [
|
Rooms: [
|
||||||
{ Name: "ElevatorLanding", MaxCapacity: 1600 },
|
{ Name: "ElevatorLanding", MaxCapacity: 1600 },
|
||||||
{ Name: "ApartmentRoomA", MaxCapacity: 1000 },
|
{ Name: "ApartmentRoomA", MaxCapacity: 1000 },
|
||||||
@ -83,13 +131,19 @@ const apartmentDefault: IApartment = {
|
|||||||
{ Name: "DuviriHallway", MaxCapacity: 1600 }
|
{ Name: "DuviriHallway", MaxCapacity: 1600 }
|
||||||
],
|
],
|
||||||
FavouriteLoadouts: [],
|
FavouriteLoadouts: [],
|
||||||
Gardening: {}
|
Gardening: {
|
||||||
|
Planters: []
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const orbiterSchema = new Schema<IOrbiter>(
|
const orbiterSchema = new Schema<IOrbiter>(
|
||||||
{
|
{
|
||||||
Features: [String],
|
Features: [String],
|
||||||
Rooms: [roomSchema],
|
Rooms: [roomSchema],
|
||||||
|
VignetteFish: { type: [String], default: undefined },
|
||||||
|
FavouriteLoadoutId: Schema.Types.ObjectId,
|
||||||
|
Wallpaper: String,
|
||||||
|
Vignette: String,
|
||||||
ContentUrlSignature: { type: String, required: false },
|
ContentUrlSignature: { type: String, required: false },
|
||||||
BootLocation: String
|
BootLocation: String
|
||||||
},
|
},
|
||||||
@ -107,21 +161,6 @@ const orbiterDefault: IOrbiter = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const favouriteLoadoutSchema = new Schema<IFavouriteLoadoutDatabase>(
|
|
||||||
{
|
|
||||||
Tag: String,
|
|
||||||
LoadoutId: Schema.Types.ObjectId
|
|
||||||
},
|
|
||||||
{ _id: false }
|
|
||||||
);
|
|
||||||
favouriteLoadoutSchema.set("toJSON", {
|
|
||||||
virtuals: true,
|
|
||||||
transform(_document, returnedObject) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
||||||
returnedObject.LoadoutId = toOid(returnedObject.LoadoutId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const tailorShopSchema = new Schema<ITailorShopDatabase>(
|
const tailorShopSchema = new Schema<ITailorShopDatabase>(
|
||||||
{
|
{
|
||||||
FavouriteLoadouts: [favouriteLoadoutSchema],
|
FavouriteLoadouts: [favouriteLoadoutSchema],
|
||||||
|
@ -4,6 +4,7 @@ import { abortDojoComponentController } from "@/src/controllers/api/abortDojoCom
|
|||||||
import { abortDojoComponentDestructionController } from "@/src/controllers/api/abortDojoComponentDestructionController";
|
import { abortDojoComponentDestructionController } from "@/src/controllers/api/abortDojoComponentDestructionController";
|
||||||
import { activateRandomModController } from "@/src/controllers/api/activateRandomModController";
|
import { activateRandomModController } from "@/src/controllers/api/activateRandomModController";
|
||||||
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
|
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
|
||||||
|
import { addIgnoredUserController } from "@/src/controllers/api/addIgnoredUserController";
|
||||||
import { addToAllianceController } from "@/src/controllers/api/addToAllianceController";
|
import { addToAllianceController } from "@/src/controllers/api/addToAllianceController";
|
||||||
import { addToGuildController } from "@/src/controllers/api/addToGuildController";
|
import { addToGuildController } from "@/src/controllers/api/addToGuildController";
|
||||||
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
|
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
|
||||||
@ -18,6 +19,7 @@ import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompl
|
|||||||
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";
|
||||||
|
import { completeCalendarEventController } from "@/src/controllers/api/completeCalendarEventController";
|
||||||
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
|
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
|
||||||
import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController";
|
import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController";
|
||||||
import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController";
|
import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController";
|
||||||
@ -27,6 +29,8 @@ import { contributeToVaultController } from "@/src/controllers/api/contributeToV
|
|||||||
import { createAllianceController } from "@/src/controllers/api/createAllianceController";
|
import { createAllianceController } from "@/src/controllers/api/createAllianceController";
|
||||||
import { createGuildController } from "@/src/controllers/api/createGuildController";
|
import { createGuildController } from "@/src/controllers/api/createGuildController";
|
||||||
import { creditsController } from "@/src/controllers/api/creditsController";
|
import { creditsController } from "@/src/controllers/api/creditsController";
|
||||||
|
import { crewMembersController } from "@/src/controllers/api/crewMembersController";
|
||||||
|
import { crewShipIdentifySalvageController } from "@/src/controllers/api/crewShipIdentifySalvageController";
|
||||||
import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController";
|
import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController";
|
||||||
import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController";
|
import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController";
|
||||||
import { declineAllianceInviteController } from "@/src/controllers/api/declineAllianceInviteController";
|
import { declineAllianceInviteController } from "@/src/controllers/api/declineAllianceInviteController";
|
||||||
@ -35,7 +39,7 @@ import { deleteSessionController } from "@/src/controllers/api/deleteSessionCont
|
|||||||
import { destroyDojoDecoController } from "@/src/controllers/api/destroyDojoDecoController";
|
import { destroyDojoDecoController } from "@/src/controllers/api/destroyDojoDecoController";
|
||||||
import { divvyAllianceVaultController } from "@/src/controllers/api/divvyAllianceVaultController";
|
import { divvyAllianceVaultController } from "@/src/controllers/api/divvyAllianceVaultController";
|
||||||
import { dojoComponentRushController } from "@/src/controllers/api/dojoComponentRushController";
|
import { dojoComponentRushController } from "@/src/controllers/api/dojoComponentRushController";
|
||||||
import { dojoController } from "@/src/controllers/api/dojoController";
|
import { dojoController, setDojoURLController } from "@/src/controllers/api/dojoController";
|
||||||
import { dronesController } from "@/src/controllers/api/dronesController";
|
import { dronesController } from "@/src/controllers/api/dronesController";
|
||||||
import { endlessXpController } from "@/src/controllers/api/endlessXpController";
|
import { endlessXpController } from "@/src/controllers/api/endlessXpController";
|
||||||
import { entratiLabConquestModeController } from "@/src/controllers/api/entratiLabConquestModeController";
|
import { entratiLabConquestModeController } from "@/src/controllers/api/entratiLabConquestModeController";
|
||||||
@ -44,6 +48,7 @@ import { findSessionsController } from "@/src/controllers/api/findSessionsContro
|
|||||||
import { fishmongerController } from "@/src/controllers/api/fishmongerController";
|
import { fishmongerController } from "@/src/controllers/api/fishmongerController";
|
||||||
import { focusController } from "@/src/controllers/api/focusController";
|
import { focusController } from "@/src/controllers/api/focusController";
|
||||||
import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
|
import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
|
||||||
|
import { gardeningController } from "@/src/controllers/api/gardeningController";
|
||||||
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
|
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
|
||||||
import { getAllianceController } from "@/src/controllers/api/getAllianceController";
|
import { getAllianceController } from "@/src/controllers/api/getAllianceController";
|
||||||
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
|
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
|
||||||
@ -61,7 +66,8 @@ import { giftingController } from "@/src/controllers/api/giftingController";
|
|||||||
import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
|
import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
|
||||||
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController";
|
||||||
import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
|
import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController";
|
||||||
import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKey";
|
import { giveQuestKeyRewardController } from "@/src/controllers/api/giveQuestKeyRewardController";
|
||||||
|
import { giveShipDecoAndLoreFragmentController } from "@/src/controllers/api/giveShipDecoAndLoreFragmentController";
|
||||||
import { giveStartingGearController } from "@/src/controllers/api/giveStartingGearController";
|
import { giveStartingGearController } from "@/src/controllers/api/giveStartingGearController";
|
||||||
import { guildTechController } from "@/src/controllers/api/guildTechController";
|
import { guildTechController } from "@/src/controllers/api/guildTechController";
|
||||||
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
|
import { hostSessionController } from "@/src/controllers/api/hostSessionController";
|
||||||
@ -85,6 +91,7 @@ import { modularWeaponSaleController } from "@/src/controllers/api/modularWeapon
|
|||||||
import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
|
import { nameWeaponController } from "@/src/controllers/api/nameWeaponController";
|
||||||
import { nemesisController } from "@/src/controllers/api/nemesisController";
|
import { nemesisController } from "@/src/controllers/api/nemesisController";
|
||||||
import { placeDecoInComponentController } from "@/src/controllers/api/placeDecoInComponentController";
|
import { placeDecoInComponentController } from "@/src/controllers/api/placeDecoInComponentController";
|
||||||
|
import { playedParkourTutorialController } from "@/src/controllers/api/playedParkourTutorialController";
|
||||||
import { playerSkillsController } from "@/src/controllers/api/playerSkillsController";
|
import { playerSkillsController } from "@/src/controllers/api/playerSkillsController";
|
||||||
import { postGuildAdvertisementController } from "@/src/controllers/api/postGuildAdvertisementController";
|
import { postGuildAdvertisementController } from "@/src/controllers/api/postGuildAdvertisementController";
|
||||||
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
|
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
|
||||||
@ -94,13 +101,15 @@ import { redeemPromoCodeController } from "@/src/controllers/api/redeemPromoCode
|
|||||||
import { releasePetController } from "@/src/controllers/api/releasePetController";
|
import { releasePetController } from "@/src/controllers/api/releasePetController";
|
||||||
import { removeFromAllianceController } from "@/src/controllers/api/removeFromAllianceController";
|
import { removeFromAllianceController } from "@/src/controllers/api/removeFromAllianceController";
|
||||||
import { removeFromGuildController } from "@/src/controllers/api/removeFromGuildController";
|
import { removeFromGuildController } from "@/src/controllers/api/removeFromGuildController";
|
||||||
|
import { removeIgnoredUserController } from "@/src/controllers/api/removeIgnoredUserController";
|
||||||
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
|
import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController";
|
||||||
import { retrievePetFromStasisController } from "@/src/controllers/api/retrievePetFromStasisController";
|
import { retrievePetFromStasisController } from "@/src/controllers/api/retrievePetFromStasisController";
|
||||||
import { saveDialogueController } from "@/src/controllers/api/saveDialogueController";
|
import { saveDialogueController } from "@/src/controllers/api/saveDialogueController";
|
||||||
import { saveLoadoutController } from "@/src/controllers/api/saveLoadout";
|
import { saveLoadoutController } from "@/src/controllers/api/saveLoadoutController";
|
||||||
import { saveSettingsController } from "@/src/controllers/api/saveSettingsController";
|
import { saveSettingsController } from "@/src/controllers/api/saveSettingsController";
|
||||||
import { saveVaultAutoContributeController } from "@/src/controllers/api/saveVaultAutoContributeController";
|
import { saveVaultAutoContributeController } from "@/src/controllers/api/saveVaultAutoContributeController";
|
||||||
import { sellController } from "@/src/controllers/api/sellController";
|
import { sellController } from "@/src/controllers/api/sellController";
|
||||||
|
import { sendMsgToInBoxController } from "@/src/controllers/api/sendMsgToInBoxController";
|
||||||
import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestController";
|
import { setActiveQuestController } from "@/src/controllers/api/setActiveQuestController";
|
||||||
import { setActiveShipController } from "@/src/controllers/api/setActiveShipController";
|
import { setActiveShipController } from "@/src/controllers/api/setActiveShipController";
|
||||||
import { setAllianceGuildPermissionsController } from "@/src/controllers/api/setAllianceGuildPermissionsController";
|
import { setAllianceGuildPermissionsController } from "@/src/controllers/api/setAllianceGuildPermissionsController";
|
||||||
@ -110,9 +119,11 @@ import { setDojoComponentMessageController } from "@/src/controllers/api/setDojo
|
|||||||
import { setDojoComponentSettingsController } from "@/src/controllers/api/setDojoComponentSettingsController";
|
import { setDojoComponentSettingsController } from "@/src/controllers/api/setDojoComponentSettingsController";
|
||||||
import { setEquippedInstrumentController } from "@/src/controllers/api/setEquippedInstrumentController";
|
import { setEquippedInstrumentController } from "@/src/controllers/api/setEquippedInstrumentController";
|
||||||
import { setGuildMotdController } from "@/src/controllers/api/setGuildMotdController";
|
import { setGuildMotdController } from "@/src/controllers/api/setGuildMotdController";
|
||||||
|
import { setHubNpcCustomizationsController } from "@/src/controllers/api/setHubNpcCustomizationsController";
|
||||||
import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDecoInfoController";
|
import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDecoInfoController";
|
||||||
import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController";
|
import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController";
|
||||||
import { setShipFavouriteLoadoutController } from "@/src/controllers/api/setShipFavouriteLoadoutController";
|
import { setShipFavouriteLoadoutController } from "@/src/controllers/api/setShipFavouriteLoadoutController";
|
||||||
|
import { setShipVignetteController } from "@/src/controllers/api/setShipVignetteController";
|
||||||
import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController";
|
import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController";
|
||||||
import { setWeaponSkillTreeController } from "@/src/controllers/api/setWeaponSkillTreeController";
|
import { setWeaponSkillTreeController } from "@/src/controllers/api/setWeaponSkillTreeController";
|
||||||
import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController";
|
import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController";
|
||||||
@ -145,9 +156,11 @@ const apiRouter = express.Router();
|
|||||||
apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController);
|
apiRouter.get("/abandonLibraryDailyTask.php", abandonLibraryDailyTaskController);
|
||||||
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
|
apiRouter.get("/abortDojoComponentDestruction.php", abortDojoComponentDestructionController);
|
||||||
apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementController);
|
apiRouter.get("/cancelGuildAdvertisement.php", cancelGuildAdvertisementController);
|
||||||
|
apiRouter.get("/changeDojoRoot.php", changeDojoRootController);
|
||||||
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
|
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
|
||||||
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
|
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
|
||||||
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
|
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
|
||||||
|
apiRouter.get("/completeCalendarEvent.php", completeCalendarEventController);
|
||||||
apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController);
|
apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController);
|
||||||
apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController);
|
apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController);
|
||||||
apiRouter.get("/credits.php", creditsController);
|
apiRouter.get("/credits.php", creditsController);
|
||||||
@ -176,12 +189,14 @@ apiRouter.get("/logout.php", logoutController);
|
|||||||
apiRouter.get("/marketRecommendations.php", marketRecommendationsController);
|
apiRouter.get("/marketRecommendations.php", marketRecommendationsController);
|
||||||
apiRouter.get("/marketSearchRecommendations.php", marketRecommendationsController);
|
apiRouter.get("/marketSearchRecommendations.php", marketRecommendationsController);
|
||||||
apiRouter.get("/modularWeaponSale.php", modularWeaponSaleController);
|
apiRouter.get("/modularWeaponSale.php", modularWeaponSaleController);
|
||||||
|
apiRouter.get("/playedParkourTutorial.php", playedParkourTutorialController);
|
||||||
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
|
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
|
||||||
apiRouter.get("/removeFromAlliance.php", removeFromAllianceController);
|
apiRouter.get("/removeFromAlliance.php", removeFromAllianceController);
|
||||||
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
|
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
|
||||||
apiRouter.get("/setActiveShip.php", setActiveShipController);
|
apiRouter.get("/setActiveShip.php", setActiveShipController);
|
||||||
apiRouter.get("/setAllianceGuildPermissions.php", setAllianceGuildPermissionsController);
|
apiRouter.get("/setAllianceGuildPermissions.php", setAllianceGuildPermissionsController);
|
||||||
apiRouter.get("/setBootLocation.php", setBootLocationController);
|
apiRouter.get("/setBootLocation.php", setBootLocationController);
|
||||||
|
apiRouter.get("/setDojoURL", setDojoURLController);
|
||||||
apiRouter.get("/setGuildMotd.php", setGuildMotdController);
|
apiRouter.get("/setGuildMotd.php", setGuildMotdController);
|
||||||
apiRouter.get("/setSupportedSyndicate.php", setSupportedSyndicateController);
|
apiRouter.get("/setSupportedSyndicate.php", setSupportedSyndicateController);
|
||||||
apiRouter.get("/startLibraryDailyTask.php", startLibraryDailyTaskController);
|
apiRouter.get("/startLibraryDailyTask.php", startLibraryDailyTaskController);
|
||||||
@ -194,6 +209,7 @@ apiRouter.get("/updateSession.php", updateSessionGetController);
|
|||||||
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
|
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
|
||||||
apiRouter.post("/activateRandomMod.php", activateRandomModController);
|
apiRouter.post("/activateRandomMod.php", activateRandomModController);
|
||||||
apiRouter.post("/addFriendImage.php", addFriendImageController);
|
apiRouter.post("/addFriendImage.php", addFriendImageController);
|
||||||
|
apiRouter.post("/addIgnoredUser.php", addIgnoredUserController);
|
||||||
apiRouter.post("/addToAlliance.php", addToAllianceController);
|
apiRouter.post("/addToAlliance.php", addToAllianceController);
|
||||||
apiRouter.post("/addToGuild.php", addToGuildController);
|
apiRouter.post("/addToGuild.php", addToGuildController);
|
||||||
apiRouter.post("/arcaneCommon.php", arcaneCommonController);
|
apiRouter.post("/arcaneCommon.php", arcaneCommonController);
|
||||||
@ -211,6 +227,8 @@ apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentContro
|
|||||||
apiRouter.post("/contributeToVault.php", contributeToVaultController);
|
apiRouter.post("/contributeToVault.php", contributeToVaultController);
|
||||||
apiRouter.post("/createAlliance.php", createAllianceController);
|
apiRouter.post("/createAlliance.php", createAllianceController);
|
||||||
apiRouter.post("/createGuild.php", createGuildController);
|
apiRouter.post("/createGuild.php", createGuildController);
|
||||||
|
apiRouter.post("/crewMembers.php", crewMembersController);
|
||||||
|
apiRouter.post("/crewShipIdentifySalvage.php", crewShipIdentifySalvageController);
|
||||||
apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController);
|
apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController);
|
||||||
apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController);
|
apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController);
|
||||||
apiRouter.post("/destroyDojoDeco.php", destroyDojoDecoController);
|
apiRouter.post("/destroyDojoDeco.php", destroyDojoDecoController);
|
||||||
@ -223,6 +241,7 @@ apiRouter.post("/findSessions.php", findSessionsController);
|
|||||||
apiRouter.post("/fishmonger.php", fishmongerController);
|
apiRouter.post("/fishmonger.php", fishmongerController);
|
||||||
apiRouter.post("/focus.php", focusController);
|
apiRouter.post("/focus.php", focusController);
|
||||||
apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
|
apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
|
||||||
|
apiRouter.post("/gardening.php", gardeningController);
|
||||||
apiRouter.post("/genericUpdate.php", genericUpdateController);
|
apiRouter.post("/genericUpdate.php", genericUpdateController);
|
||||||
apiRouter.post("/getAlliance.php", getAllianceController);
|
apiRouter.post("/getAlliance.php", getAllianceController);
|
||||||
apiRouter.post("/getFriends.php", getFriendsController);
|
apiRouter.post("/getFriends.php", getFriendsController);
|
||||||
@ -233,6 +252,7 @@ apiRouter.post("/gildWeapon.php", gildWeaponController);
|
|||||||
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
|
apiRouter.post("/giveKeyChainTriggeredItems.php", giveKeyChainTriggeredItemsController);
|
||||||
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
|
apiRouter.post("/giveKeyChainTriggeredMessage.php", giveKeyChainTriggeredMessageController);
|
||||||
apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
|
apiRouter.post("/giveQuestKeyReward.php", giveQuestKeyRewardController);
|
||||||
|
apiRouter.post("/giveShipDecoAndLoreFragment.php", giveShipDecoAndLoreFragmentController);
|
||||||
apiRouter.post("/giveStartingGear.php", giveStartingGearController);
|
apiRouter.post("/giveStartingGear.php", giveStartingGearController);
|
||||||
apiRouter.post("/guildTech.php", guildTechController);
|
apiRouter.post("/guildTech.php", guildTechController);
|
||||||
apiRouter.post("/hostSession.php", hostSessionController);
|
apiRouter.post("/hostSession.php", hostSessionController);
|
||||||
@ -256,6 +276,7 @@ apiRouter.post("/purchase.php", purchaseController);
|
|||||||
apiRouter.post("/redeemPromoCode.php", redeemPromoCodeController);
|
apiRouter.post("/redeemPromoCode.php", redeemPromoCodeController);
|
||||||
apiRouter.post("/releasePet.php", releasePetController);
|
apiRouter.post("/releasePet.php", releasePetController);
|
||||||
apiRouter.post("/removeFromGuild.php", removeFromGuildController);
|
apiRouter.post("/removeFromGuild.php", removeFromGuildController);
|
||||||
|
apiRouter.post("/removeIgnoredUser.php", removeIgnoredUserController);
|
||||||
apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
|
apiRouter.post("/rerollRandomMod.php", rerollRandomModController);
|
||||||
apiRouter.post("/retrievePetFromStasis.php", retrievePetFromStasisController);
|
apiRouter.post("/retrievePetFromStasis.php", retrievePetFromStasisController);
|
||||||
apiRouter.post("/saveDialogue.php", saveDialogueController);
|
apiRouter.post("/saveDialogue.php", saveDialogueController);
|
||||||
@ -263,14 +284,17 @@ apiRouter.post("/saveLoadout.php", saveLoadoutController);
|
|||||||
apiRouter.post("/saveSettings.php", saveSettingsController);
|
apiRouter.post("/saveSettings.php", saveSettingsController);
|
||||||
apiRouter.post("/saveVaultAutoContribute.php", saveVaultAutoContributeController);
|
apiRouter.post("/saveVaultAutoContribute.php", saveVaultAutoContributeController);
|
||||||
apiRouter.post("/sell.php", sellController);
|
apiRouter.post("/sell.php", sellController);
|
||||||
|
apiRouter.post("/sendMsgToInBox.php", sendMsgToInBoxController);
|
||||||
apiRouter.post("/setDojoComponentColors.php", setDojoComponentColorsController);
|
apiRouter.post("/setDojoComponentColors.php", setDojoComponentColorsController);
|
||||||
apiRouter.post("/setDojoComponentMessage.php", setDojoComponentMessageController);
|
apiRouter.post("/setDojoComponentMessage.php", setDojoComponentMessageController);
|
||||||
apiRouter.post("/setDojoComponentSettings.php", setDojoComponentSettingsController);
|
apiRouter.post("/setDojoComponentSettings.php", setDojoComponentSettingsController);
|
||||||
apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController);
|
apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController);
|
||||||
apiRouter.post("/setGuildMotd.php", setGuildMotdController);
|
apiRouter.post("/setGuildMotd.php", setGuildMotdController);
|
||||||
|
apiRouter.post("/setHubNpcCustomizations.php", setHubNpcCustomizationsController);
|
||||||
apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController);
|
apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController);
|
||||||
apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController);
|
apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController);
|
||||||
apiRouter.post("/setShipFavouriteLoadout.php", setShipFavouriteLoadoutController);
|
apiRouter.post("/setShipFavouriteLoadout.php", setShipFavouriteLoadoutController);
|
||||||
|
apiRouter.post("/setShipVignette.php", setShipVignetteController);
|
||||||
apiRouter.post("/setWeaponSkillTree.php", setWeaponSkillTreeController);
|
apiRouter.post("/setWeaponSkillTree.php", setWeaponSkillTreeController);
|
||||||
apiRouter.post("/shipDecorations.php", shipDecorationsController);
|
apiRouter.post("/shipDecorations.php", shipDecorationsController);
|
||||||
apiRouter.post("/startCollectibleEntry.php", startCollectibleEntryController);
|
apiRouter.post("/startCollectibleEntry.php", startCollectibleEntryController);
|
||||||
|
@ -10,19 +10,19 @@ import { getAccountInfoController } from "@/src/controllers/custom/getAccountInf
|
|||||||
import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
|
import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
|
||||||
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
|
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
|
||||||
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
|
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
|
||||||
|
import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
|
||||||
|
|
||||||
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";
|
||||||
import { addCurrencyController } from "@/src/controllers/custom/addCurrencyController";
|
import { addCurrencyController } from "@/src/controllers/custom/addCurrencyController";
|
||||||
import { addItemsController } from "@/src/controllers/custom/addItemsController";
|
import { addItemsController } from "@/src/controllers/custom/addItemsController";
|
||||||
import { addModularEquipmentController } from "@/src/controllers/custom/addModularEquipmentController";
|
|
||||||
import { addXpController } from "@/src/controllers/custom/addXpController";
|
import { addXpController } from "@/src/controllers/custom/addXpController";
|
||||||
import { gildEquipmentController } from "@/src/controllers/custom/gildEquipmentController";
|
|
||||||
import { importController } from "@/src/controllers/custom/importController";
|
import { importController } from "@/src/controllers/custom/importController";
|
||||||
|
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
|
||||||
|
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
|
||||||
|
|
||||||
import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
|
import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
|
||||||
import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
|
import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
|
||||||
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
|
|
||||||
|
|
||||||
const customRouter = express.Router();
|
const customRouter = express.Router();
|
||||||
|
|
||||||
@ -36,16 +36,16 @@ customRouter.get("/getAccountInfo", getAccountInfoController);
|
|||||||
customRouter.get("/renameAccount", renameAccountController);
|
customRouter.get("/renameAccount", renameAccountController);
|
||||||
customRouter.get("/ircDropped", ircDroppedController);
|
customRouter.get("/ircDropped", ircDroppedController);
|
||||||
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
|
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
|
||||||
|
customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
|
||||||
|
|
||||||
customRouter.post("/createAccount", createAccountController);
|
customRouter.post("/createAccount", createAccountController);
|
||||||
customRouter.post("/createMessage", createMessageController);
|
customRouter.post("/createMessage", createMessageController);
|
||||||
customRouter.post("/addCurrency", addCurrencyController);
|
customRouter.post("/addCurrency", addCurrencyController);
|
||||||
customRouter.post("/addItems", addItemsController);
|
customRouter.post("/addItems", addItemsController);
|
||||||
customRouter.post("/addModularEquipment", addModularEquipmentController);
|
|
||||||
customRouter.post("/addXp", addXpController);
|
customRouter.post("/addXp", addXpController);
|
||||||
customRouter.post("/gildEquipment", gildEquipmentController);
|
|
||||||
customRouter.post("/import", importController);
|
customRouter.post("/import", importController);
|
||||||
customRouter.post("/manageQuests", manageQuestsController);
|
customRouter.post("/manageQuests", manageQuestsController);
|
||||||
|
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
|
||||||
|
|
||||||
customRouter.get("/config", getConfigDataController);
|
customRouter.get("/config", getConfigDataController);
|
||||||
customRouter.post("/config", updateConfigDataController);
|
customRouter.post("/config", updateConfigDataController);
|
||||||
|
@ -24,21 +24,29 @@ interface IConfig {
|
|||||||
infiniteEndo?: boolean;
|
infiniteEndo?: boolean;
|
||||||
infiniteRegalAya?: boolean;
|
infiniteRegalAya?: boolean;
|
||||||
infiniteHelminthMaterials?: boolean;
|
infiniteHelminthMaterials?: boolean;
|
||||||
|
dontSubtractConsumables?: boolean;
|
||||||
unlockAllShipFeatures?: boolean;
|
unlockAllShipFeatures?: boolean;
|
||||||
unlockAllShipDecorations?: boolean;
|
unlockAllShipDecorations?: boolean;
|
||||||
unlockAllFlavourItems?: boolean;
|
unlockAllFlavourItems?: boolean;
|
||||||
unlockAllSkins?: boolean;
|
unlockAllSkins?: boolean;
|
||||||
unlockAllCapturaScenes?: boolean;
|
unlockAllCapturaScenes?: boolean;
|
||||||
|
unlockAllDecoRecipes?: boolean;
|
||||||
universalPolarityEverywhere?: boolean;
|
universalPolarityEverywhere?: boolean;
|
||||||
unlockDoubleCapacityPotatoesEverywhere?: boolean;
|
unlockDoubleCapacityPotatoesEverywhere?: boolean;
|
||||||
unlockExilusEverywhere?: boolean;
|
unlockExilusEverywhere?: boolean;
|
||||||
unlockArcanesEverywhere?: boolean;
|
unlockArcanesEverywhere?: boolean;
|
||||||
noDailyStandingLimits?: boolean;
|
noDailyStandingLimits?: boolean;
|
||||||
|
noDailyFocusLimit?: boolean;
|
||||||
noArgonCrystalDecay?: boolean;
|
noArgonCrystalDecay?: boolean;
|
||||||
noMasteryRankUpCooldown?: boolean;
|
noMasteryRankUpCooldown?: boolean;
|
||||||
noVendorPurchaseLimits?: boolean;
|
noVendorPurchaseLimits?: boolean;
|
||||||
|
noDeathMarks?: boolean;
|
||||||
|
noKimCooldowns?: boolean;
|
||||||
instantResourceExtractorDrones?: boolean;
|
instantResourceExtractorDrones?: boolean;
|
||||||
|
noResourceExtractorDronesDamage?: boolean;
|
||||||
|
skipClanKeyCrafting?: boolean;
|
||||||
noDojoRoomBuildStage?: boolean;
|
noDojoRoomBuildStage?: boolean;
|
||||||
|
noDojoDecoBuildStage?: boolean;
|
||||||
fastDojoRoomDestruction?: boolean;
|
fastDojoRoomDestruction?: boolean;
|
||||||
noDojoResearchCosts?: boolean;
|
noDojoResearchCosts?: boolean;
|
||||||
noDojoResearchTime?: boolean;
|
noDojoResearchTime?: boolean;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||||
import { getInventory } from "@/src/services/inventoryService";
|
import { addLevelKeys, addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||||
import { Alliance, AllianceMember, Guild, GuildAd, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
|
import { Alliance, AllianceMember, Guild, GuildAd, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
|
||||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||||
import {
|
import {
|
||||||
@ -30,6 +30,8 @@ import { Inbox } from "../models/inboxModel";
|
|||||||
import { IFusionTreasure, ITypeCount } from "../types/inventoryTypes/inventoryTypes";
|
import { IFusionTreasure, ITypeCount } from "../types/inventoryTypes/inventoryTypes";
|
||||||
import { IInventoryChanges } from "../types/purchaseTypes";
|
import { IInventoryChanges } from "../types/purchaseTypes";
|
||||||
import { parallelForeach } from "../utils/async-utils";
|
import { parallelForeach } from "../utils/async-utils";
|
||||||
|
import allDecoRecipes from "@/static/fixed_responses/allDecoRecipes.json";
|
||||||
|
import { createMessage } from "./inboxService";
|
||||||
|
|
||||||
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
|
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
|
||||||
const accountId = await getAccountIdForRequest(req);
|
const accountId = await getAccountIdForRequest(req);
|
||||||
@ -57,6 +59,7 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
|||||||
|
|
||||||
const members: IGuildMemberClient[] = [];
|
const members: IGuildMemberClient[] = [];
|
||||||
let missingEntry = true;
|
let missingEntry = true;
|
||||||
|
const dataFillInPromises: Promise<void>[] = [];
|
||||||
for (const guildMember of guildMembers) {
|
for (const guildMember of guildMembers) {
|
||||||
const member: IGuildMemberClient = {
|
const member: IGuildMemberClient = {
|
||||||
_id: toOid(guildMember.accountId),
|
_id: toOid(guildMember.accountId),
|
||||||
@ -68,8 +71,12 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
|||||||
if (guildMember.accountId.equals(accountId)) {
|
if (guildMember.accountId.equals(accountId)) {
|
||||||
missingEntry = false;
|
missingEntry = false;
|
||||||
} else {
|
} else {
|
||||||
member.DisplayName = (await Account.findById(guildMember.accountId, "DisplayName"))!.DisplayName;
|
dataFillInPromises.push(
|
||||||
await fillInInventoryDataForGuildMember(member);
|
(async (): Promise<void> => {
|
||||||
|
member.DisplayName = (await Account.findById(guildMember.accountId, "DisplayName"))!.DisplayName;
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
dataFillInPromises.push(fillInInventoryDataForGuildMember(member));
|
||||||
}
|
}
|
||||||
members.push(member);
|
members.push(member);
|
||||||
}
|
}
|
||||||
@ -88,6 +95,8 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Promise.all(dataFillInPromises);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_id: toOid(guild._id),
|
_id: toOid(guild._id),
|
||||||
Name: guild.Name,
|
Name: guild.Name,
|
||||||
@ -96,6 +105,7 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
|||||||
Members: members,
|
Members: members,
|
||||||
Ranks: guild.Ranks,
|
Ranks: guild.Ranks,
|
||||||
Tier: guild.Tier,
|
Tier: guild.Tier,
|
||||||
|
Emblem: guild.Emblem,
|
||||||
Vault: getGuildVault(guild),
|
Vault: getGuildVault(guild),
|
||||||
ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
|
ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
|
||||||
Class: guild.Class,
|
Class: guild.Class,
|
||||||
@ -114,14 +124,17 @@ export const getGuildVault = (guild: TGuildDatabaseDocument): IGuildVault => {
|
|||||||
DojoRefundMiscItems: guild.VaultMiscItems,
|
DojoRefundMiscItems: guild.VaultMiscItems,
|
||||||
DojoRefundPremiumCredits: guild.VaultPremiumCredits,
|
DojoRefundPremiumCredits: guild.VaultPremiumCredits,
|
||||||
ShipDecorations: guild.VaultShipDecorations,
|
ShipDecorations: guild.VaultShipDecorations,
|
||||||
FusionTreasures: guild.VaultFusionTreasures
|
FusionTreasures: guild.VaultFusionTreasures,
|
||||||
|
DecoRecipes: config.unlockAllDecoRecipes
|
||||||
|
? allDecoRecipes.map(recipe => ({ ItemType: recipe, ItemCount: 1 }))
|
||||||
|
: guild.VaultDecoRecipes
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDojoClient = async (
|
export const getDojoClient = async (
|
||||||
guild: TGuildDatabaseDocument,
|
guild: TGuildDatabaseDocument,
|
||||||
status: number,
|
status: number,
|
||||||
componentId: Types.ObjectId | string | undefined = undefined
|
componentId?: Types.ObjectId | string
|
||||||
): Promise<IDojoClient> => {
|
): Promise<IDojoClient> => {
|
||||||
const dojo: IDojoClient = {
|
const dojo: IDojoClient = {
|
||||||
_id: { $oid: guild._id.toString() },
|
_id: { $oid: guild._id.toString() },
|
||||||
@ -147,6 +160,7 @@ export const getDojoClient = async (
|
|||||||
if (!componentId || dojoComponent._id.equals(componentId)) {
|
if (!componentId || dojoComponent._id.equals(componentId)) {
|
||||||
const clientComponent: IDojoComponentClient = {
|
const clientComponent: IDojoComponentClient = {
|
||||||
id: toOid(dojoComponent._id),
|
id: toOid(dojoComponent._id),
|
||||||
|
SortId: toOid(dojoComponent.SortId ?? dojoComponent._id), // always providing a SortId so decos don't need repositioning to reparent
|
||||||
pf: dojoComponent.pf,
|
pf: dojoComponent.pf,
|
||||||
ppf: dojoComponent.ppf,
|
ppf: dojoComponent.ppf,
|
||||||
Name: dojoComponent.Name,
|
Name: dojoComponent.Name,
|
||||||
@ -209,6 +223,7 @@ export const getDojoClient = async (
|
|||||||
Type: deco.Type,
|
Type: deco.Type,
|
||||||
Pos: deco.Pos,
|
Pos: deco.Pos,
|
||||||
Rot: deco.Rot,
|
Rot: deco.Rot,
|
||||||
|
Scale: deco.Scale,
|
||||||
Name: deco.Name,
|
Name: deco.Name,
|
||||||
Sockets: deco.Sockets,
|
Sockets: deco.Sockets,
|
||||||
PictureFrameInfo: deco.PictureFrameInfo
|
PictureFrameInfo: deco.PictureFrameInfo
|
||||||
@ -490,7 +505,7 @@ export const hasGuildPermissionEx = (
|
|||||||
|
|
||||||
export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise<void> => {
|
export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise<void> => {
|
||||||
const members = await GuildMember.find({ guildId, status: 0 }, "accountId");
|
const members = await GuildMember.find({ guildId, status: 0 }, "accountId");
|
||||||
for (const member of members) {
|
await parallelForeach(members, async member => {
|
||||||
const inventory = await getInventory(member.accountId.toString(), "MiscItems");
|
const inventory = await getInventory(member.accountId.toString(), "MiscItems");
|
||||||
const index = inventory.MiscItems.findIndex(
|
const index = inventory.MiscItems.findIndex(
|
||||||
x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment"
|
x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment"
|
||||||
@ -499,7 +514,7 @@ export const removePigmentsFromGuildMembers = async (guildId: string | Types.Obj
|
|||||||
inventory.MiscItems.splice(index, 1);
|
inventory.MiscItems.splice(index, 1);
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const processGuildTechProjectContributionsUpdate = async (
|
export const processGuildTechProjectContributionsUpdate = async (
|
||||||
@ -539,7 +554,7 @@ export const setGuildTechLogState = (
|
|||||||
guild: TGuildDatabaseDocument,
|
guild: TGuildDatabaseDocument,
|
||||||
type: string,
|
type: string,
|
||||||
state: number,
|
state: number,
|
||||||
dateTime: Date | undefined = undefined
|
dateTime?: Date
|
||||||
): boolean => {
|
): boolean => {
|
||||||
guild.TechChanges ??= [];
|
guild.TechChanges ??= [];
|
||||||
const entry = guild.TechChanges.find(x => x.details == type);
|
const entry = guild.TechChanges.find(x => x.details == type);
|
||||||
@ -596,6 +611,76 @@ const setGuildTier = async (guild: TGuildDatabaseDocument, newTier: number): Pro
|
|||||||
await processGuildTechProjectContributionsUpdate(guild, project);
|
await processGuildTechProjectContributionsUpdate(guild, project);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (guild.CeremonyContributors) {
|
||||||
|
await checkClanAscensionHasRequiredContributors(guild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkClanAscensionHasRequiredContributors = async (guild: TGuildDatabaseDocument): Promise<void> => {
|
||||||
|
const requiredContributors = [1, 5, 15, 30, 50][guild.Tier - 1];
|
||||||
|
// Once required contributor count is hit, the class is committed and there's 72 hours to claim endo.
|
||||||
|
if (guild.CeremonyContributors!.length >= requiredContributors) {
|
||||||
|
guild.Class = guild.CeremonyClass!;
|
||||||
|
guild.CeremonyClass = undefined;
|
||||||
|
guild.CeremonyResetDate = new Date(Date.now() + (config.fastClanAscension ? 5_000 : 72 * 3600_000));
|
||||||
|
if (!config.fastClanAscension) {
|
||||||
|
// Send message to all active guild members
|
||||||
|
const members = await GuildMember.find({ guildId: guild._id, status: 0 }, "accountId");
|
||||||
|
await parallelForeach(members, async member => {
|
||||||
|
// somewhat unfaithful as on live the "msg" is not a loctag, but since we don't have the string, we'll let the client fill it in with "arg".
|
||||||
|
await createMessage(member.accountId, [
|
||||||
|
{
|
||||||
|
sndr: guild.Name,
|
||||||
|
msg: "/Lotus/Language/Clan/Clan_AscensionCeremonyInProgressDetails",
|
||||||
|
arg: [
|
||||||
|
{
|
||||||
|
Key: "RESETDATE",
|
||||||
|
Tag:
|
||||||
|
guild.CeremonyResetDate!.getUTCMonth() +
|
||||||
|
"/" +
|
||||||
|
guild.CeremonyResetDate!.getUTCDate() +
|
||||||
|
"/" +
|
||||||
|
(guild.CeremonyResetDate!.getUTCFullYear() % 100) +
|
||||||
|
" " +
|
||||||
|
guild.CeremonyResetDate!.getUTCHours().toString().padStart(2, "0") +
|
||||||
|
":" +
|
||||||
|
guild.CeremonyResetDate!.getUTCMinutes().toString().padStart(2, "0")
|
||||||
|
}
|
||||||
|
],
|
||||||
|
sub: "/Lotus/Language/Clan/Clan_AscensionCeremonyInProgress",
|
||||||
|
icon: "/Lotus/Interface/Graphics/ClanTileImages/ClanEnterDojo.png",
|
||||||
|
highPriority: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const giveClanKey = (inventory: TInventoryDatabaseDocument, inventoryChanges?: IInventoryChanges): void => {
|
||||||
|
if (config.skipClanKeyCrafting) {
|
||||||
|
const levelKeyChanges = [
|
||||||
|
{
|
||||||
|
ItemType: "/Lotus/Types/Keys/DojoKey",
|
||||||
|
ItemCount: 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
addLevelKeys(inventory, levelKeyChanges);
|
||||||
|
if (inventoryChanges) {
|
||||||
|
combineInventoryChanges(inventoryChanges, { LevelKeys: levelKeyChanges });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const recipeChanges = [
|
||||||
|
{
|
||||||
|
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||||
|
ItemCount: 1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
addRecipes(inventory, recipeChanges);
|
||||||
|
if (inventoryChanges) {
|
||||||
|
combineInventoryChanges(inventoryChanges, { Recipes: recipeChanges });
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeDojoKeyItems = (inventory: TInventoryDatabaseDocument): IInventoryChanges => {
|
export const removeDojoKeyItems = (inventory: TInventoryDatabaseDocument): IInventoryChanges => {
|
||||||
|
@ -2,6 +2,7 @@ import { Types } from "mongoose";
|
|||||||
import {
|
import {
|
||||||
IEquipmentClient,
|
IEquipmentClient,
|
||||||
IEquipmentDatabase,
|
IEquipmentDatabase,
|
||||||
|
IItemConfig,
|
||||||
IOperatorConfigClient,
|
IOperatorConfigClient,
|
||||||
IOperatorConfigDatabase
|
IOperatorConfigDatabase
|
||||||
} from "../types/inventoryTypes/commonInventoryTypes";
|
} from "../types/inventoryTypes/commonInventoryTypes";
|
||||||
@ -37,6 +38,7 @@ import {
|
|||||||
} from "../types/inventoryTypes/inventoryTypes";
|
} from "../types/inventoryTypes/inventoryTypes";
|
||||||
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
|
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
|
||||||
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "../types/saveLoadoutTypes";
|
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "../types/saveLoadoutTypes";
|
||||||
|
import { slotNames } from "../types/purchaseTypes";
|
||||||
|
|
||||||
const convertDate = (value: IMongoDate): Date => {
|
const convertDate = (value: IMongoDate): Date => {
|
||||||
return new Date(parseInt(value.$date.$numberLong));
|
return new Date(parseInt(value.$date.$numberLong));
|
||||||
@ -104,18 +106,18 @@ const replaceSlots = (db: ISlots, client: ISlots): void => {
|
|||||||
db.Slots = client.Slots;
|
db.Slots = client.Slots;
|
||||||
};
|
};
|
||||||
|
|
||||||
const convertCrewShipMember = (client: ICrewShipMemberClient): ICrewShipMemberDatabase => {
|
export const importCrewMemberId = (crewMemberId: ICrewShipMemberClient): ICrewShipMemberDatabase => {
|
||||||
return {
|
if (crewMemberId.ItemId) {
|
||||||
...client,
|
return { ItemId: new Types.ObjectId(crewMemberId.ItemId.$oid) };
|
||||||
ItemId: client.ItemId ? new Types.ObjectId(client.ItemId.$oid) : undefined
|
}
|
||||||
};
|
return { NemesisFingerprint: BigInt(crewMemberId.NemesisFingerprint ?? 0) };
|
||||||
};
|
};
|
||||||
|
|
||||||
const convertCrewShipMembers = (client: ICrewShipMembersClient): ICrewShipMembersDatabase => {
|
const convertCrewShipMembers = (client: ICrewShipMembersClient): ICrewShipMembersDatabase => {
|
||||||
return {
|
return {
|
||||||
SLOT_A: client.SLOT_A ? convertCrewShipMember(client.SLOT_A) : undefined,
|
SLOT_A: client.SLOT_A ? importCrewMemberId(client.SLOT_A) : undefined,
|
||||||
SLOT_B: client.SLOT_B ? convertCrewShipMember(client.SLOT_B) : undefined,
|
SLOT_B: client.SLOT_B ? importCrewMemberId(client.SLOT_B) : undefined,
|
||||||
SLOT_C: client.SLOT_C ? convertCrewShipMember(client.SLOT_C) : undefined
|
SLOT_C: client.SLOT_C ? importCrewMemberId(client.SLOT_C) : undefined
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -168,10 +170,25 @@ const convertPendingRecipe = (client: IPendingRecipeClient): IPendingRecipeDatab
|
|||||||
const convertNemesis = (client: INemesisClient): INemesisDatabase => {
|
const convertNemesis = (client: INemesisClient): INemesisDatabase => {
|
||||||
return {
|
return {
|
||||||
...client,
|
...client,
|
||||||
|
fp: BigInt(client.fp),
|
||||||
d: convertDate(client.d)
|
d: convertDate(client.d)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Empty objects from live may have been encoded as empty arrays because of PHP.
|
||||||
|
const convertItemConfig = <T extends IItemConfig>(client: T): T => {
|
||||||
|
return {
|
||||||
|
...client,
|
||||||
|
pricol: Array.isArray(client.pricol) ? {} : client.pricol,
|
||||||
|
attcol: Array.isArray(client.attcol) ? {} : client.attcol,
|
||||||
|
sigcol: Array.isArray(client.sigcol) ? {} : client.sigcol,
|
||||||
|
eyecol: Array.isArray(client.eyecol) ? {} : client.eyecol,
|
||||||
|
facial: Array.isArray(client.facial) ? {} : client.facial,
|
||||||
|
cloth: Array.isArray(client.cloth) ? {} : client.cloth,
|
||||||
|
syancol: Array.isArray(client.syancol) ? {} : client.syancol
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): void => {
|
export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<IInventoryClient>): void => {
|
||||||
for (const key of equipmentKeys) {
|
for (const key of equipmentKeys) {
|
||||||
if (client[key] !== undefined) {
|
if (client[key] !== undefined) {
|
||||||
@ -212,35 +229,28 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
|||||||
replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
|
replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const key of [
|
for (const key of slotNames) {
|
||||||
"SuitBin",
|
|
||||||
"WeaponBin",
|
|
||||||
"SentinelBin",
|
|
||||||
"SpaceSuitBin",
|
|
||||||
"SpaceWeaponBin",
|
|
||||||
"PvpBonusLoadoutBin",
|
|
||||||
"PveBonusLoadoutBin",
|
|
||||||
"RandomModBin",
|
|
||||||
"MechBin",
|
|
||||||
"CrewMemberBin",
|
|
||||||
"OperatorAmpBin",
|
|
||||||
"CrewShipSalvageBin"
|
|
||||||
] as const) {
|
|
||||||
if (client[key] !== undefined) {
|
if (client[key] !== undefined) {
|
||||||
replaceSlots(db[key], client[key]);
|
replaceSlots(db[key], client[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// boolean
|
||||||
for (const key of [
|
for (const key of [
|
||||||
"UseAdultOperatorLoadout",
|
"UseAdultOperatorLoadout",
|
||||||
"HasOwnedVoidProjectionsPreviously",
|
"HasOwnedVoidProjectionsPreviously",
|
||||||
"ReceivedStartingGear",
|
"ReceivedStartingGear",
|
||||||
"ArchwingEnabled",
|
"ArchwingEnabled",
|
||||||
"PlayedParkourTutorial"
|
"PlayedParkourTutorial",
|
||||||
|
"Staff",
|
||||||
|
"Moderator",
|
||||||
|
"Partner",
|
||||||
|
"Counselor"
|
||||||
] as const) {
|
] as const) {
|
||||||
if (client[key] !== undefined) {
|
if (client[key] !== undefined) {
|
||||||
db[key] = client[key];
|
db[key] = client[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// number
|
||||||
for (const key of [
|
for (const key of [
|
||||||
"PlayerLevel",
|
"PlayerLevel",
|
||||||
"RegularCredits",
|
"RegularCredits",
|
||||||
@ -250,12 +260,15 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
|||||||
"PrimeTokens",
|
"PrimeTokens",
|
||||||
"TradesRemaining",
|
"TradesRemaining",
|
||||||
"GiftsRemaining",
|
"GiftsRemaining",
|
||||||
"ChallengesFixVersion"
|
"ChallengesFixVersion",
|
||||||
|
"Founder",
|
||||||
|
"Guide"
|
||||||
] as const) {
|
] as const) {
|
||||||
if (client[key] !== undefined) {
|
if (client[key] !== undefined) {
|
||||||
db[key] = client[key];
|
db[key] = client[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// string
|
||||||
for (const key of [
|
for (const key of [
|
||||||
"ThemeStyle",
|
"ThemeStyle",
|
||||||
"ThemeBackground",
|
"ThemeBackground",
|
||||||
@ -270,6 +283,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
|||||||
db[key] = client[key];
|
db[key] = client[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// string[]
|
||||||
for (const key of [
|
for (const key of [
|
||||||
"EquippedGear",
|
"EquippedGear",
|
||||||
"EquippedEmotes",
|
"EquippedEmotes",
|
||||||
@ -353,7 +367,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
|||||||
db.PlayerSkills = client.PlayerSkills;
|
db.PlayerSkills = client.PlayerSkills;
|
||||||
}
|
}
|
||||||
if (client.LotusCustomization !== undefined) {
|
if (client.LotusCustomization !== undefined) {
|
||||||
db.LotusCustomization = client.LotusCustomization;
|
db.LotusCustomization = convertItemConfig(client.LotusCustomization);
|
||||||
}
|
}
|
||||||
if (client.CollectibleSeries !== undefined) {
|
if (client.CollectibleSeries !== undefined) {
|
||||||
db.CollectibleSeries = client.CollectibleSeries;
|
db.CollectibleSeries = client.CollectibleSeries;
|
||||||
@ -380,6 +394,9 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (client.Accolades !== undefined) {
|
||||||
|
db.Accolades = client.Accolades;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const convertLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {
|
const convertLoadOutConfig = (client: ILoadoutConfigClient): ILoadoutConfigDatabase => {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,7 @@ import {
|
|||||||
dict_uk,
|
dict_uk,
|
||||||
dict_zh,
|
dict_zh,
|
||||||
ExportArcanes,
|
ExportArcanes,
|
||||||
|
ExportBoosters,
|
||||||
ExportCustoms,
|
ExportCustoms,
|
||||||
ExportDrones,
|
ExportDrones,
|
||||||
ExportGear,
|
ExportGear,
|
||||||
@ -185,14 +186,15 @@ export const getKeyChainMessage = ({ KeyChain, ChainStage }: IKeyChainRequest):
|
|||||||
throw new Error(`KeyChain ${KeyChain} does not contain chain stages`);
|
throw new Error(`KeyChain ${KeyChain} does not contain chain stages`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyChainStage = chainStages[ChainStage];
|
let i = ChainStage;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
let chainStageMessage = chainStages[i].messageToSendWhenTriggered;
|
||||||
if (!keyChainStage) {
|
while (!chainStageMessage) {
|
||||||
throw new Error(`KeyChainStage ${ChainStage} not found`);
|
if (++i >= chainStages.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
chainStageMessage = chainStages[i].messageToSendWhenTriggered;
|
||||||
}
|
}
|
||||||
|
|
||||||
const chainStageMessage = keyChainStage.messageToSendWhenTriggered;
|
|
||||||
|
|
||||||
if (!chainStageMessage) {
|
if (!chainStageMessage) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`client requested key chain message in keychain ${KeyChain} at stage ${ChainStage} but they did not exist`
|
`client requested key chain message in keychain ${KeyChain} at stage ${ChainStage} but they did not exist`
|
||||||
@ -216,15 +218,30 @@ export const convertInboxMessage = (message: IInboxMessage): IMessage => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const isStoreItem = (type: string): boolean => {
|
export const isStoreItem = (type: string): boolean => {
|
||||||
return type.startsWith("/Lotus/StoreItems/");
|
return type.startsWith("/Lotus/StoreItems/") || type in ExportBoosters;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toStoreItem = (type: string): string => {
|
export const toStoreItem = (type: string): string => {
|
||||||
|
if (type.startsWith("/Lotus/Types/StoreItems/Boosters/")) {
|
||||||
|
const boosterEntry = Object.entries(ExportBoosters).find(arr => arr[1].typeName == type);
|
||||||
|
if (boosterEntry) {
|
||||||
|
return boosterEntry[0];
|
||||||
|
}
|
||||||
|
throw new Error(`could not convert ${type} to a store item`);
|
||||||
|
}
|
||||||
return "/Lotus/StoreItems/" + type.substring("/Lotus/".length);
|
return "/Lotus/StoreItems/" + type.substring("/Lotus/".length);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fromStoreItem = (type: string): string => {
|
export const fromStoreItem = (type: string): string => {
|
||||||
return "/Lotus/" + type.substring("/Lotus/StoreItems/".length);
|
if (type.startsWith("/Lotus/StoreItems/")) {
|
||||||
|
return "/Lotus/" + type.substring("/Lotus/StoreItems/".length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type in ExportBoosters) {
|
||||||
|
return ExportBoosters[type].typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`${type} is not a store item`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDefaultUpgrades = (parts: string[]): IDefaultUpgrade[] | undefined => {
|
export const getDefaultUpgrades = (parts: string[]): IDefaultUpgrade[] | undefined => {
|
||||||
|
@ -8,7 +8,7 @@ export const submitLeaderboardScore = async (
|
|||||||
ownerId: string,
|
ownerId: string,
|
||||||
displayName: string,
|
displayName: string,
|
||||||
score: number,
|
score: number,
|
||||||
guildId?: string
|
guildId: string | undefined
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
let expiry: Date;
|
let expiry: Date;
|
||||||
if (schedule == "daily") {
|
if (schedule == "daily") {
|
||||||
@ -39,9 +39,9 @@ export const getLeaderboard = async (
|
|||||||
leaderboard: string,
|
leaderboard: string,
|
||||||
before: number,
|
before: number,
|
||||||
after: number,
|
after: number,
|
||||||
pivotId?: string,
|
pivotId: string | undefined,
|
||||||
guildId?: string,
|
guildId: string | undefined,
|
||||||
guildTier?: number
|
guildTier: number | undefined
|
||||||
): Promise<ILeaderboardEntryClient[]> => {
|
): Promise<ILeaderboardEntryClient[]> => {
|
||||||
const filter: { leaderboard: string; guildId?: string; guildTier?: number } = { leaderboard };
|
const filter: { leaderboard: string; guildId?: string; guildTier?: number } = { leaderboard };
|
||||||
if (guildId) {
|
if (guildId) {
|
||||||
|
@ -77,7 +77,6 @@ const getRandomLoginReward = (rng: CRng, day: number, inventory: TInventoryDatab
|
|||||||
const reward = rng.randomReward(randomRewards)!;
|
const reward = rng.randomReward(randomRewards)!;
|
||||||
//const reward = randomRewards.find(x => x.RewardType == "RT_BOOSTER")!;
|
//const reward = randomRewards.find(x => x.RewardType == "RT_BOOSTER")!;
|
||||||
if (reward.RewardType == "RT_RANDOM_RECIPE") {
|
if (reward.RewardType == "RT_RANDOM_RECIPE") {
|
||||||
// Not very faithful implementation but roughly the same idea
|
|
||||||
const masteredItems = new Set();
|
const masteredItems = new Set();
|
||||||
for (const entry of inventory.XPInfo) {
|
for (const entry of inventory.XPInfo) {
|
||||||
masteredItems.add(entry.ItemType);
|
masteredItems.add(entry.ItemType);
|
||||||
@ -95,15 +94,15 @@ const getRandomLoginReward = (rng: CRng, day: number, inventory: TInventoryDatab
|
|||||||
}
|
}
|
||||||
const eligibleRecipes: string[] = [];
|
const eligibleRecipes: string[] = [];
|
||||||
for (const [uniqueName, recipe] of Object.entries(ExportRecipes)) {
|
for (const [uniqueName, recipe] of Object.entries(ExportRecipes)) {
|
||||||
if (unmasteredItems.has(recipe.resultType)) {
|
if (!recipe.excludeFromMarket && unmasteredItems.has(recipe.resultType)) {
|
||||||
eligibleRecipes.push(uniqueName);
|
eligibleRecipes.push(uniqueName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (eligibleRecipes.length == 0) {
|
if (eligibleRecipes.length == 0) {
|
||||||
// This account has all warframes and weapons already mastered (filthy cheater), need a different reward.
|
// This account has all applicable warframes and weapons already mastered (filthy cheater), need a different reward.
|
||||||
return getRandomLoginReward(rng, day, inventory);
|
return getRandomLoginReward(rng, day, inventory);
|
||||||
}
|
}
|
||||||
reward.StoreItemType = toStoreItem(rng.randomElement(eligibleRecipes));
|
reward.StoreItemType = toStoreItem(rng.randomElement(eligibleRecipes)!);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
//_id: toOid(new Types.ObjectId()),
|
//_id: toOid(new Types.ObjectId()),
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,14 @@
|
|||||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
|
import { PersonalRooms } from "@/src/models/personalRoomsModel";
|
||||||
import { addItem, getInventory } from "@/src/services/inventoryService";
|
import { addItem, getInventory } from "@/src/services/inventoryService";
|
||||||
import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes";
|
import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes";
|
||||||
|
import { IGardeningDatabase } from "../types/shipTypes";
|
||||||
|
import { getRandomElement } from "./rngService";
|
||||||
|
|
||||||
export const getPersonalRooms = async (accountId: string): Promise<TPersonalRoomsDatabaseDocument> => {
|
export const getPersonalRooms = async (
|
||||||
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
|
accountId: string,
|
||||||
|
projection?: string
|
||||||
|
): Promise<TPersonalRoomsDatabaseDocument> => {
|
||||||
|
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId }, projection);
|
||||||
|
|
||||||
if (!personalRooms) {
|
if (!personalRooms) {
|
||||||
throw new Error(`personal rooms not found for account ${accountId}`);
|
throw new Error(`personal rooms not found for account ${accountId}`);
|
||||||
@ -25,3 +30,64 @@ export const updateShipFeature = async (accountId: string, shipFeature: string):
|
|||||||
await addItem(inventory, shipFeature, -1);
|
await addItem(inventory, shipFeature, -1);
|
||||||
await inventory.save();
|
await inventory.save();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createGarden = (): IGardeningDatabase => {
|
||||||
|
const plantTypes = [
|
||||||
|
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantA",
|
||||||
|
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantB",
|
||||||
|
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantC",
|
||||||
|
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantD",
|
||||||
|
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantE",
|
||||||
|
"/Lotus/Types/Items/Plants/MiscItems/DuvxDuviriGrowingPlantF"
|
||||||
|
];
|
||||||
|
const endTime = new Date((Math.trunc(Date.now() / 1000) + 79200) * 1000); // Plants will take 22 hours to grow
|
||||||
|
return {
|
||||||
|
Planters: [
|
||||||
|
{
|
||||||
|
Name: "Garden0",
|
||||||
|
Plants: [
|
||||||
|
{
|
||||||
|
PlantType: getRandomElement(plantTypes)!,
|
||||||
|
EndTime: endTime,
|
||||||
|
PlotIndex: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PlantType: getRandomElement(plantTypes)!,
|
||||||
|
EndTime: endTime,
|
||||||
|
PlotIndex: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Garden1",
|
||||||
|
Plants: [
|
||||||
|
{
|
||||||
|
PlantType: getRandomElement(plantTypes)!,
|
||||||
|
EndTime: endTime,
|
||||||
|
PlotIndex: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PlantType: getRandomElement(plantTypes)!,
|
||||||
|
EndTime: endTime,
|
||||||
|
PlotIndex: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Garden2",
|
||||||
|
Plants: [
|
||||||
|
{
|
||||||
|
PlantType: getRandomElement(plantTypes)!,
|
||||||
|
EndTime: endTime,
|
||||||
|
PlotIndex: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PlantType: getRandomElement(plantTypes)!,
|
||||||
|
EndTime: endTime,
|
||||||
|
PlotIndex: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -66,6 +66,18 @@ export const handlePurchase = async (
|
|||||||
if (!offer) {
|
if (!offer) {
|
||||||
throw new Error(`unknown vendor offer: ${ItemId ? ItemId : purchaseRequest.PurchaseParams.StoreItem}`);
|
throw new Error(`unknown vendor offer: ${ItemId ? ItemId : purchaseRequest.PurchaseParams.StoreItem}`);
|
||||||
}
|
}
|
||||||
|
if (offer.RegularPrice) {
|
||||||
|
combineInventoryChanges(
|
||||||
|
prePurchaseInventoryChanges,
|
||||||
|
updateCurrency(inventory, offer.RegularPrice[0], false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (offer.PremiumPrice) {
|
||||||
|
combineInventoryChanges(
|
||||||
|
prePurchaseInventoryChanges,
|
||||||
|
updateCurrency(inventory, offer.PremiumPrice[0], true)
|
||||||
|
);
|
||||||
|
}
|
||||||
if (offer.ItemPrices) {
|
if (offer.ItemPrices) {
|
||||||
handleItemPrices(
|
handleItemPrices(
|
||||||
inventory,
|
inventory,
|
||||||
@ -141,7 +153,8 @@ export const handlePurchase = async (
|
|||||||
inventory,
|
inventory,
|
||||||
purchaseRequest.PurchaseParams.Quantity,
|
purchaseRequest.PurchaseParams.Quantity,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
false,
|
||||||
|
purchaseRequest.PurchaseParams.UsePremium,
|
||||||
seed
|
seed
|
||||||
);
|
);
|
||||||
combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
|
combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
|
||||||
@ -169,6 +182,9 @@ export const handlePurchase = async (
|
|||||||
purchaseResponse.InventoryChanges,
|
purchaseResponse.InventoryChanges,
|
||||||
updateCurrency(inventory, offer.RegularPrice, false)
|
updateCurrency(inventory, offer.RegularPrice, false)
|
||||||
);
|
);
|
||||||
|
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
|
||||||
|
throw new Error(`vendor purchase should not have an expected price`);
|
||||||
|
}
|
||||||
|
|
||||||
const invItem: IMiscItem = {
|
const invItem: IMiscItem = {
|
||||||
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
|
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
|
||||||
@ -222,12 +238,18 @@ export const handlePurchase = async (
|
|||||||
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
|
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
|
||||||
const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem);
|
const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem);
|
||||||
if (offer) {
|
if (offer) {
|
||||||
if (offer.credits) {
|
if (typeof offer.credits == "number") {
|
||||||
combineInventoryChanges(
|
combineInventoryChanges(
|
||||||
purchaseResponse.InventoryChanges,
|
purchaseResponse.InventoryChanges,
|
||||||
updateCurrency(inventory, offer.credits, false)
|
updateCurrency(inventory, offer.credits, false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (typeof offer.platinum == "number") {
|
||||||
|
combineInventoryChanges(
|
||||||
|
purchaseResponse.InventoryChanges,
|
||||||
|
updateCurrency(inventory, offer.platinum, true)
|
||||||
|
);
|
||||||
|
}
|
||||||
if (offer.itemPrices) {
|
if (offer.itemPrices) {
|
||||||
handleItemPrices(
|
handleItemPrices(
|
||||||
inventory,
|
inventory,
|
||||||
@ -238,6 +260,9 @@ export const handlePurchase = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
|
||||||
|
throw new Error(`vendor purchase should not have an expected price`);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 18: {
|
case 18: {
|
||||||
if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) {
|
if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) {
|
||||||
@ -331,6 +356,7 @@ export const handleStoreItemAcquisition = async (
|
|||||||
quantity: number = 1,
|
quantity: number = 1,
|
||||||
durability: TRarity = "COMMON",
|
durability: TRarity = "COMMON",
|
||||||
ignorePurchaseQuantity: boolean = false,
|
ignorePurchaseQuantity: boolean = false,
|
||||||
|
premiumPurchase: boolean = true,
|
||||||
seed?: bigint
|
seed?: bigint
|
||||||
): Promise<IPurchaseResponse> => {
|
): Promise<IPurchaseResponse> => {
|
||||||
let purchaseResponse = {
|
let purchaseResponse = {
|
||||||
@ -352,11 +378,20 @@ export const handleStoreItemAcquisition = async (
|
|||||||
}
|
}
|
||||||
switch (storeCategory) {
|
switch (storeCategory) {
|
||||||
default: {
|
default: {
|
||||||
purchaseResponse = { InventoryChanges: await addItem(inventory, internalName, quantity, true, seed) };
|
purchaseResponse = {
|
||||||
|
InventoryChanges: await addItem(inventory, internalName, quantity, premiumPurchase, seed)
|
||||||
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "Types":
|
case "Types":
|
||||||
purchaseResponse = await handleTypesPurchase(internalName, inventory, quantity, ignorePurchaseQuantity);
|
purchaseResponse = await handleTypesPurchase(
|
||||||
|
internalName,
|
||||||
|
inventory,
|
||||||
|
quantity,
|
||||||
|
ignorePurchaseQuantity,
|
||||||
|
premiumPurchase,
|
||||||
|
seed
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "Boosters":
|
case "Boosters":
|
||||||
purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durability);
|
purchaseResponse = handleBoostersPurchase(storeItemName, inventory, durability);
|
||||||
@ -478,13 +513,15 @@ const handleTypesPurchase = async (
|
|||||||
typesName: string,
|
typesName: string,
|
||||||
inventory: TInventoryDatabaseDocument,
|
inventory: TInventoryDatabaseDocument,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
ignorePurchaseQuantity: boolean
|
ignorePurchaseQuantity: boolean,
|
||||||
|
premiumPurchase: boolean = true,
|
||||||
|
seed?: bigint
|
||||||
): Promise<IPurchaseResponse> => {
|
): Promise<IPurchaseResponse> => {
|
||||||
const typeCategory = getStoreItemTypesCategory(typesName);
|
const typeCategory = getStoreItemTypesCategory(typesName);
|
||||||
logger.debug(`type category ${typeCategory}`);
|
logger.debug(`type category ${typeCategory}`);
|
||||||
switch (typeCategory) {
|
switch (typeCategory) {
|
||||||
default:
|
default:
|
||||||
return { InventoryChanges: await addItem(inventory, typesName, quantity) };
|
return { InventoryChanges: await addItem(inventory, typesName, quantity, premiumPurchase, seed) };
|
||||||
case "BoosterPacks":
|
case "BoosterPacks":
|
||||||
return handleBoosterPackPurchase(typesName, inventory, quantity);
|
return handleBoosterPackPurchase(typesName, inventory, quantity);
|
||||||
case "SlotItems":
|
case "SlotItems":
|
||||||
|
@ -191,6 +191,25 @@ const getQuestCompletionItems = (questKey: string): ITypeCount[] | undefined =>
|
|||||||
return items;
|
return items;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Checks that `questKey` is in `requirements`, and if so, that all other quests in `requirements` are also already completed.
|
||||||
|
const doesQuestCompletionFinishSet = (
|
||||||
|
inventory: TInventoryDatabaseDocument,
|
||||||
|
questKey: string,
|
||||||
|
requirements: string[]
|
||||||
|
): boolean => {
|
||||||
|
let holds = false;
|
||||||
|
for (const requirement of requirements) {
|
||||||
|
if (questKey == requirement) {
|
||||||
|
holds = true;
|
||||||
|
} else {
|
||||||
|
if (!inventory.QuestKeys.find(x => x.ItemType == requirement)?.Completed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return holds;
|
||||||
|
};
|
||||||
|
|
||||||
const handleQuestCompletion = async (
|
const handleQuestCompletion = async (
|
||||||
inventory: TInventoryDatabaseDocument,
|
inventory: TInventoryDatabaseDocument,
|
||||||
questKey: string,
|
questKey: string,
|
||||||
@ -216,6 +235,44 @@ const handleQuestCompletion = async (
|
|||||||
setupKahlSyndicate(inventory);
|
setupKahlSyndicate(inventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whispers in the Walls is unlocked once The New + Heart of Deimos are completed.
|
||||||
|
if (
|
||||||
|
doesQuestCompletionFinishSet(inventory, questKey, [
|
||||||
|
"/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain",
|
||||||
|
"/Lotus/Types/Keys/InfestedMicroplanetQuest/InfestedMicroplanetQuestKeyChain"
|
||||||
|
])
|
||||||
|
) {
|
||||||
|
await createMessage(inventory.accountOwnerId, [
|
||||||
|
{
|
||||||
|
sndr: "/Lotus/Language/Bosses/Loid",
|
||||||
|
msg: "/Lotus/Language/EntratiLab/EntratiQuest/WiTWQuestRecievedInboxBody",
|
||||||
|
att: ["/Lotus/Types/Keys/EntratiLab/EntratiQuestKeyChain"],
|
||||||
|
sub: "/Lotus/Language/EntratiLab/EntratiQuest/WiTWQuestRecievedInboxTitle",
|
||||||
|
icon: "/Lotus/Interface/Icons/Npcs/Entrati/Loid.png",
|
||||||
|
highPriority: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Hex (Quest) is unlocked once The Lotus Eaters + The Duviri Paradox are completed.
|
||||||
|
if (
|
||||||
|
doesQuestCompletionFinishSet(inventory, questKey, [
|
||||||
|
"/Lotus/Types/Keys/1999PrologueQuest/1999PrologueQuestKeyChain",
|
||||||
|
"/Lotus/Types/Keys/DuviriQuest/DuviriQuestKeyChain"
|
||||||
|
])
|
||||||
|
) {
|
||||||
|
await createMessage(inventory.accountOwnerId, [
|
||||||
|
{
|
||||||
|
sndr: "/Lotus/Language/NewWar/P3M1ChooseMara",
|
||||||
|
msg: "/Lotus/Language/1999Quest/1999QuestInboxBody",
|
||||||
|
att: ["/Lotus/Types/Keys/1999Quest/1999QuestKeyChain"],
|
||||||
|
sub: "/Lotus/Language/1999Quest/1999QuestInboxSubject",
|
||||||
|
icon: "/Lotus/Interface/Icons/Npcs/Operator.png",
|
||||||
|
highPriority: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
const questCompletionItems = getQuestCompletionItems(questKey);
|
const questCompletionItems = getQuestCompletionItems(questKey);
|
||||||
logger.debug(`quest completion items`, questCompletionItems);
|
logger.debug(`quest completion items`, questCompletionItems);
|
||||||
if (questCompletionItems) {
|
if (questCompletionItems) {
|
||||||
@ -232,7 +289,7 @@ export const giveKeyChainItem = async (
|
|||||||
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
|
const inventoryChanges = await addKeyChainItems(inventory, keyChainInfo);
|
||||||
|
|
||||||
if (isEmptyObject(inventoryChanges)) {
|
if (isEmptyObject(inventoryChanges)) {
|
||||||
throw new Error("inventory changes was empty after getting keychain items: should not happen");
|
logger.warn("inventory changes was empty after getting keychain items: should not happen");
|
||||||
}
|
}
|
||||||
// items were added: update quest stage's i (item was given)
|
// items were added: update quest stage's i (item was given)
|
||||||
updateQuestStage(inventory, keyChainInfo, { i: true });
|
updateQuestStage(inventory, keyChainInfo, { i: true });
|
||||||
|
@ -6,7 +6,7 @@ export interface IRngResult {
|
|||||||
probability: number;
|
probability: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getRandomElement = <T>(arr: T[]): T => {
|
export const getRandomElement = <T>(arr: readonly T[]): T | undefined => {
|
||||||
return arr[Math.floor(Math.random() * arr.length)];
|
return arr[Math.floor(Math.random() * arr.length)];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -18,7 +18,10 @@ export const getRandomInt = (min: number, max: number): number => {
|
|||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], percentage: number): T | undefined => {
|
export const getRewardAtPercentage = <T extends { probability: number }>(
|
||||||
|
pool: T[],
|
||||||
|
percentage: number
|
||||||
|
): T | undefined => {
|
||||||
if (pool.length == 0) return;
|
if (pool.length == 0) return;
|
||||||
|
|
||||||
const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
|
const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
|
||||||
@ -31,7 +34,7 @@ const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], per
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error("What the fuck?");
|
return pool[pool.length - 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRandomReward = <T extends { probability: number }>(pool: T[]): T | undefined => {
|
export const getRandomReward = <T extends { probability: number }>(pool: T[]): T | undefined => {
|
||||||
@ -97,18 +100,32 @@ export class CRng {
|
|||||||
}
|
}
|
||||||
|
|
||||||
randomInt(min: number, max: number): number {
|
randomInt(min: number, max: number): number {
|
||||||
min = Math.ceil(min);
|
const diff = max - min;
|
||||||
max = Math.floor(max);
|
if (diff != 0) {
|
||||||
return Math.floor(this.random() * (max - min + 1)) + min;
|
if (diff < 0) {
|
||||||
|
throw new Error(`max must be greater than min`);
|
||||||
|
}
|
||||||
|
if (diff > 0x3fffffff) {
|
||||||
|
throw new Error(`insufficient entropy`);
|
||||||
|
}
|
||||||
|
min += Math.floor(this.random() * (diff + 1));
|
||||||
|
}
|
||||||
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
randomElement<T>(arr: T[]): T {
|
randomElement<T>(arr: readonly T[]): T | undefined {
|
||||||
return arr[Math.floor(this.random() * arr.length)];
|
return arr[Math.floor(this.random() * arr.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
|
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
|
||||||
return getRewardAtPercentage(pool, this.random());
|
return getRewardAtPercentage(pool, this.random());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
churnSeed(its: number): void {
|
||||||
|
while (its--) {
|
||||||
|
this.state = (this.state * 1103515245 + 12345) & 0x7fffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seeded RNG for cases where we need identical results to the game client. Based on work by Donald Knuth.
|
// Seeded RNG for cases where we need identical results to the game client. Based on work by Donald Knuth.
|
||||||
@ -128,7 +145,7 @@ export class SRng {
|
|||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
randomElement<T>(arr: T[]): T {
|
randomElement<T>(arr: readonly T[]): T | undefined {
|
||||||
return arr[this.randomInt(0, arr.length - 1)];
|
return arr[this.randomInt(0, arr.length - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,4 +153,8 @@ export class SRng {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
randomReward<T extends { probability: number }>(pool: T[]): T | undefined {
|
||||||
|
return getRewardAtPercentage(pool, this.randomFloat());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import { Types } from "mongoose";
|
|||||||
import { isEmptyObject } from "@/src/helpers/general";
|
import { isEmptyObject } from "@/src/helpers/general";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||||
|
import { IItemConfig } from "../types/inventoryTypes/commonInventoryTypes";
|
||||||
|
import { importCrewMemberId } from "./importService";
|
||||||
|
|
||||||
//TODO: setup default items on account creation or like originally in giveStartingItems.php
|
//TODO: setup default items on account creation or like originally in giveStartingItems.php
|
||||||
|
|
||||||
@ -140,10 +142,30 @@ export const handleInventoryItemConfigChange = async (
|
|||||||
case "WeaponSkins": {
|
case "WeaponSkins": {
|
||||||
const itemEntries = equipment as IItemEntry;
|
const itemEntries = equipment as IItemEntry;
|
||||||
for (const [itemId, itemConfigEntries] of Object.entries(itemEntries)) {
|
for (const [itemId, itemConfigEntries] of Object.entries(itemEntries)) {
|
||||||
inventory.WeaponSkins.id(itemId)!.IsNew = itemConfigEntries.IsNew;
|
if (itemId.startsWith("ca70ca70ca70ca70")) {
|
||||||
|
logger.warn(
|
||||||
|
`unlockAllSkins does not work with favoriting items because you don't actually own it`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const inventoryItem = inventory.WeaponSkins.id(itemId);
|
||||||
|
if (!inventoryItem) {
|
||||||
|
throw new Error(`inventory item WeaponSkins not found with id ${itemId}`);
|
||||||
|
}
|
||||||
|
if ("Favorite" in itemConfigEntries) {
|
||||||
|
inventoryItem.Favorite = itemConfigEntries.Favorite;
|
||||||
|
}
|
||||||
|
if ("IsNew" in itemConfigEntries) {
|
||||||
|
inventoryItem.IsNew = itemConfigEntries.IsNew;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "LotusCustomization": {
|
||||||
|
logger.debug(`saved LotusCustomization`, equipmentChanges.LotusCustomization);
|
||||||
|
inventory.LotusCustomization = equipmentChanges.LotusCustomization;
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
if (equipmentKeys.includes(equipmentName as TEquipmentKey) && equipmentName != "ValidNewLoadoutId") {
|
if (equipmentKeys.includes(equipmentName as TEquipmentKey) && equipmentName != "ValidNewLoadoutId") {
|
||||||
logger.debug(`general Item config saved of type ${equipmentName}`, {
|
logger.debug(`general Item config saved of type ${equipmentName}`, {
|
||||||
@ -159,13 +181,36 @@ export const handleInventoryItemConfigChange = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const [configId, config] of Object.entries(itemConfigEntries)) {
|
for (const [configId, config] of Object.entries(itemConfigEntries)) {
|
||||||
if (typeof config !== "boolean") {
|
if (/^[0-9]+$/.test(configId)) {
|
||||||
inventoryItem.Configs[parseInt(configId)] = config;
|
inventoryItem.Configs[parseInt(configId)] = config as IItemConfig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ("Favorite" in itemConfigEntries) {
|
||||||
|
inventoryItem.Favorite = itemConfigEntries.Favorite;
|
||||||
|
}
|
||||||
if ("IsNew" in itemConfigEntries) {
|
if ("IsNew" in itemConfigEntries) {
|
||||||
inventoryItem.IsNew = itemConfigEntries.IsNew;
|
inventoryItem.IsNew = itemConfigEntries.IsNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("ItemName" in itemConfigEntries) {
|
||||||
|
inventoryItem.ItemName = itemConfigEntries.ItemName;
|
||||||
|
}
|
||||||
|
if ("RailjackImage" in itemConfigEntries) {
|
||||||
|
inventoryItem.RailjackImage = itemConfigEntries.RailjackImage;
|
||||||
|
}
|
||||||
|
if ("Customization" in itemConfigEntries) {
|
||||||
|
inventoryItem.Customization = itemConfigEntries.Customization;
|
||||||
|
}
|
||||||
|
if ("Weapon" in itemConfigEntries) {
|
||||||
|
inventoryItem.Weapon = itemConfigEntries.Weapon;
|
||||||
|
}
|
||||||
|
if (itemConfigEntries.CrewMembers) {
|
||||||
|
inventoryItem.CrewMembers = {
|
||||||
|
SLOT_A: importCrewMemberId(itemConfigEntries.CrewMembers.SLOT_A ?? {}),
|
||||||
|
SLOT_B: importCrewMemberId(itemConfigEntries.CrewMembers.SLOT_B ?? {}),
|
||||||
|
SLOT_C: importCrewMemberId(itemConfigEntries.CrewMembers.SLOT_C ?? {})
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,143 +1,308 @@
|
|||||||
import fs from "fs";
|
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||||
import path from "path";
|
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||||
import { repoDir } from "@/src/helpers/pathHelper";
|
|
||||||
import { CRng, mixSeeds } from "@/src/services/rngService";
|
import { CRng, mixSeeds } from "@/src/services/rngService";
|
||||||
import { IMongoDate } from "@/src/types/commonTypes";
|
import { IMongoDate } from "@/src/types/commonTypes";
|
||||||
import { IItemManifestPreprocessed, IRawVendorManifest, IVendorManifestPreprocessed } from "@/src/types/vendorTypes";
|
import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
|
||||||
import { JSONParse } from "json-with-bigint";
|
import { ExportVendors, IRange } from "warframe-public-export-plus";
|
||||||
import { ExportVendors } from "warframe-public-export-plus";
|
|
||||||
|
|
||||||
const getVendorManifestJson = (name: string): IRawVendorManifest => {
|
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
|
||||||
return JSONParse(fs.readFileSync(path.join(repoDir, `static/fixed_responses/getVendorInfo/${name}.json`), "utf-8"));
|
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
|
||||||
};
|
import DeimosHivemindCommisionsManifestFishmonger from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestFishmonger.json";
|
||||||
|
import DeimosHivemindCommisionsManifestPetVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestPetVendor.json";
|
||||||
|
import DeimosHivemindCommisionsManifestProspector from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestProspector.json";
|
||||||
|
import DeimosHivemindCommisionsManifestTokenVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestTokenVendor.json";
|
||||||
|
import DeimosHivemindCommisionsManifestWeaponsmith from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestWeaponsmith.json";
|
||||||
|
import DeimosHivemindTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosHivemindTokenVendorManifest.json";
|
||||||
|
import DeimosPetVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosPetVendorManifest.json";
|
||||||
|
import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosProspectorVendorManifest.json";
|
||||||
|
import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json";
|
||||||
|
import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json";
|
||||||
|
import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json";
|
||||||
|
import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json";
|
||||||
|
import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
|
||||||
|
import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json";
|
||||||
|
import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
|
||||||
|
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
|
||||||
|
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
|
||||||
|
import RadioLegionIntermission12VendorManifest from "@/static/fixed_responses/getVendorInfo/RadioLegionIntermission12VendorManifest.json";
|
||||||
|
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
|
||||||
|
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
|
||||||
|
import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json";
|
||||||
|
import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json";
|
||||||
|
import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json";
|
||||||
|
|
||||||
const rawVendorManifests: IRawVendorManifest[] = [
|
const rawVendorManifests: IVendorManifest[] = [
|
||||||
getVendorManifestJson("ArchimedeanVendorManifest"),
|
ArchimedeanVendorManifest,
|
||||||
getVendorManifestJson("DeimosEntratiFragmentVendorProductsManifest"),
|
DeimosEntratiFragmentVendorProductsManifest,
|
||||||
getVendorManifestJson("DeimosFishmongerVendorManifest"),
|
DeimosHivemindCommisionsManifestFishmonger,
|
||||||
getVendorManifestJson("DeimosHivemindCommisionsManifestFishmonger"),
|
DeimosHivemindCommisionsManifestPetVendor,
|
||||||
getVendorManifestJson("DeimosHivemindCommisionsManifestPetVendor"),
|
DeimosHivemindCommisionsManifestProspector,
|
||||||
getVendorManifestJson("DeimosHivemindCommisionsManifestProspector"),
|
DeimosHivemindCommisionsManifestTokenVendor,
|
||||||
getVendorManifestJson("DeimosHivemindCommisionsManifestTokenVendor"),
|
DeimosHivemindCommisionsManifestWeaponsmith,
|
||||||
getVendorManifestJson("DeimosHivemindCommisionsManifestWeaponsmith"),
|
DeimosHivemindTokenVendorManifest,
|
||||||
getVendorManifestJson("DeimosHivemindTokenVendorManifest"),
|
DeimosPetVendorManifest,
|
||||||
getVendorManifestJson("DeimosPetVendorManifest"),
|
DeimosProspectorVendorManifest,
|
||||||
getVendorManifestJson("DeimosProspectorVendorManifest"),
|
DuviriAcrithisVendorManifest,
|
||||||
getVendorManifestJson("DuviriAcrithisVendorManifest"),
|
EntratiLabsEntratiLabsCommisionsManifest,
|
||||||
getVendorManifestJson("EntratiLabsEntratiLabsCommisionsManifest"),
|
EntratiLabsEntratiLabVendorManifest,
|
||||||
getVendorManifestJson("EntratiLabsEntratiLabVendorManifest"),
|
HubsIronwakeDondaVendorManifest, // uses preprocessing
|
||||||
getVendorManifestJson("GuildAdvertisementVendorManifest"), // uses preprocessing
|
HubsRailjackCrewMemberVendorManifest,
|
||||||
getVendorManifestJson("HubsIronwakeDondaVendorManifest"), // uses preprocessing
|
MaskSalesmanManifest,
|
||||||
getVendorManifestJson("HubsPerrinSequenceWeaponVendorManifest"),
|
Nova1999ConquestShopManifest,
|
||||||
getVendorManifestJson("HubsRailjackCrewMemberVendorManifest"),
|
OstronPetVendorManifest,
|
||||||
getVendorManifestJson("MaskSalesmanManifest"),
|
OstronProspectorVendorManifest,
|
||||||
getVendorManifestJson("Nova1999ConquestShopManifest"),
|
RadioLegionIntermission12VendorManifest,
|
||||||
getVendorManifestJson("OstronFishmongerVendorManifest"),
|
SolarisDebtTokenVendorRepossessionsManifest,
|
||||||
getVendorManifestJson("OstronPetVendorManifest"),
|
SolarisProspectorVendorManifest,
|
||||||
getVendorManifestJson("OstronProspectorVendorManifest"),
|
Temple1999VendorManifest,
|
||||||
getVendorManifestJson("RadioLegionIntermission12VendorManifest"),
|
TeshinHardModeVendorManifest, // uses preprocessing
|
||||||
getVendorManifestJson("SolarisDebtTokenVendorManifest"),
|
ZarimanCommisionsManifestArchimedean
|
||||||
getVendorManifestJson("SolarisDebtTokenVendorRepossessionsManifest"),
|
|
||||||
getVendorManifestJson("SolarisFishmongerVendorManifest"),
|
|
||||||
getVendorManifestJson("SolarisProspectorVendorManifest"),
|
|
||||||
getVendorManifestJson("TeshinHardModeVendorManifest"), // uses preprocessing
|
|
||||||
getVendorManifestJson("ZarimanCommisionsManifestArchimedean")
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getVendorManifestByTypeName = (typeName: string): IVendorManifestPreprocessed | undefined => {
|
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
|
||||||
|
cycleOffset?: number;
|
||||||
|
cycleDuration: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const generatableVendors: IGeneratableVendorInfo[] = [
|
||||||
|
{
|
||||||
|
_id: { $oid: "67dadc30e4b6e0e5979c8d84" },
|
||||||
|
TypeName: "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest",
|
||||||
|
RandomSeedType: "VRST_WEAPON",
|
||||||
|
RequiredGoalTag: "",
|
||||||
|
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||||
|
cycleOffset: 1740960000_000,
|
||||||
|
cycleDuration: 4 * unixTimesInMs.day
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: { $oid: "60ad3b6ec96976e97d227e19" },
|
||||||
|
TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/PerrinSequenceWeaponVendorManifest",
|
||||||
|
RandomSeedType: "VRST_WEAPON",
|
||||||
|
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||||
|
cycleOffset: 1744934400_000,
|
||||||
|
cycleDuration: 4 * unixTimesInMs.day
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: { $oid: "61ba123467e5d37975aeeb03" },
|
||||||
|
TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest",
|
||||||
|
RandomSeedType: "VRST_FLAVOUR_TEXT",
|
||||||
|
cycleDuration: unixTimesInMs.week // TODO: Auto-detect this based on the items, so we don't need to specify it explicitly.
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// _id: { $oid: "5dbb4c41e966f7886c3ce939" },
|
||||||
|
// TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest"
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
|
const getVendorOid = (typeName: string): string => {
|
||||||
|
return "5be4a159b144f3cd" + catBreadHash(typeName).toString(16).padStart(8, "0");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
|
||||||
for (const vendorManifest of rawVendorManifests) {
|
for (const vendorManifest of rawVendorManifests) {
|
||||||
if (vendorManifest.VendorInfo.TypeName == typeName) {
|
if (vendorManifest.VendorInfo.TypeName == typeName) {
|
||||||
return preprocessVendorManifest(vendorManifest);
|
return preprocessVendorManifest(vendorManifest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeName == "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest") {
|
for (const vendorInfo of generatableVendors) {
|
||||||
return generateCodaWeaponVendorManifest();
|
if (vendorInfo.TypeName == typeName) {
|
||||||
|
return generateVendorManifest(vendorInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeName in ExportVendors) {
|
||||||
|
return generateVendorManifest({
|
||||||
|
_id: { $oid: getVendorOid(typeName) },
|
||||||
|
TypeName: typeName,
|
||||||
|
RandomSeedType: ExportVendors[typeName].randomSeedType,
|
||||||
|
cycleDuration: unixTimesInMs.hour
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getVendorManifestByOid = (oid: string): IVendorManifestPreprocessed | undefined => {
|
export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => {
|
||||||
for (const vendorManifest of rawVendorManifests) {
|
for (const vendorManifest of rawVendorManifests) {
|
||||||
if (vendorManifest.VendorInfo._id.$oid == oid) {
|
if (vendorManifest.VendorInfo._id.$oid == oid) {
|
||||||
return preprocessVendorManifest(vendorManifest);
|
return preprocessVendorManifest(vendorManifest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oid == "67dadc30e4b6e0e5979c8d84") {
|
for (const vendorInfo of generatableVendors) {
|
||||||
return generateCodaWeaponVendorManifest();
|
if (vendorInfo._id.$oid == oid) {
|
||||||
|
return generateVendorManifest(vendorInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const [typeName, manifest] of Object.entries(ExportVendors)) {
|
||||||
|
const typeNameOid = getVendorOid(typeName);
|
||||||
|
if (typeNameOid == oid) {
|
||||||
|
return generateVendorManifest({
|
||||||
|
_id: { $oid: typeNameOid },
|
||||||
|
TypeName: typeName,
|
||||||
|
RandomSeedType: manifest.randomSeedType,
|
||||||
|
cycleDuration: unixTimesInMs.hour
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const preprocessVendorManifest = (originalManifest: IRawVendorManifest): IVendorManifestPreprocessed => {
|
const preprocessVendorManifest = (originalManifest: IVendorManifest): IVendorManifest => {
|
||||||
if (Date.now() >= parseInt(originalManifest.VendorInfo.Expiry.$date.$numberLong)) {
|
if (Date.now() >= parseInt(originalManifest.VendorInfo.Expiry.$date.$numberLong)) {
|
||||||
const manifest = structuredClone(originalManifest);
|
const manifest = structuredClone(originalManifest);
|
||||||
const info = manifest.VendorInfo;
|
const info = manifest.VendorInfo;
|
||||||
refreshExpiry(info.Expiry);
|
refreshExpiry(info.Expiry);
|
||||||
for (const offer of info.ItemManifest) {
|
for (const offer of info.ItemManifest) {
|
||||||
const iteration = refreshExpiry(offer.Expiry);
|
refreshExpiry(offer.Expiry);
|
||||||
if (offer.ItemPrices) {
|
|
||||||
for (const price of offer.ItemPrices) {
|
|
||||||
if (typeof price.ItemType != "string") {
|
|
||||||
const itemSeed = parseInt(offer.Id.$oid.substring(16), 16);
|
|
||||||
const rng = new CRng(mixSeeds(itemSeed, iteration));
|
|
||||||
price.ItemType = rng.randomElement(price.ItemType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return manifest as IVendorManifestPreprocessed;
|
return manifest;
|
||||||
}
|
}
|
||||||
return originalManifest as IVendorManifestPreprocessed;
|
return originalManifest;
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshExpiry = (expiry: IMongoDate): number => {
|
const refreshExpiry = (expiry: IMongoDate): void => {
|
||||||
const period = parseInt(expiry.$date.$numberLong);
|
const period = parseInt(expiry.$date.$numberLong);
|
||||||
if (Date.now() >= period) {
|
if (Date.now() >= period) {
|
||||||
const epoch = 1734307200 * 1000; // Monday (for weekly schedules)
|
const epoch = 1734307200_000; // Monday (for weekly schedules)
|
||||||
const iteration = Math.trunc((Date.now() - epoch) / period);
|
const iteration = Math.trunc((Date.now() - epoch) / period);
|
||||||
const start = epoch + iteration * period;
|
const start = epoch + iteration * period;
|
||||||
const end = start + period;
|
const end = start + period;
|
||||||
expiry.$date.$numberLong = end.toString();
|
expiry.$date.$numberLong = end.toString();
|
||||||
return iteration;
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateCodaWeaponVendorManifest = (): IVendorManifestPreprocessed => {
|
const toRange = (value: IRange | number): IRange => {
|
||||||
const EPOCH = 1740960000 * 1000;
|
if (typeof value == "number") {
|
||||||
const DUR = 4 * 86400 * 1000;
|
return { minValue: value, maxValue: value };
|
||||||
const cycle = Math.trunc((Date.now() - EPOCH) / DUR);
|
}
|
||||||
const cycleStart = EPOCH + cycle * DUR;
|
return value;
|
||||||
const cycleEnd = cycleStart + DUR;
|
};
|
||||||
const binThisCycle = cycle % 2; // isOneBinPerCycle
|
|
||||||
const items: IItemManifestPreprocessed[] = [];
|
const vendorInfoCache: Record<string, IVendorInfo> = {};
|
||||||
const manifest = ExportVendors["/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest"];
|
|
||||||
const rng = new CRng(cycle);
|
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => {
|
||||||
for (const rawItem of manifest.items) {
|
if (!(vendorInfo.TypeName in vendorInfoCache)) {
|
||||||
if (rawItem.bin != binThisCycle) {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
continue;
|
const { cycleOffset, cycleDuration, ...clientVendorInfo } = vendorInfo;
|
||||||
|
vendorInfoCache[vendorInfo.TypeName] = {
|
||||||
|
...clientVendorInfo,
|
||||||
|
ItemManifest: [],
|
||||||
|
Expiry: { $date: { $numberLong: "0" } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const processed = vendorInfoCache[vendorInfo.TypeName];
|
||||||
|
if (Date.now() >= parseInt(processed.Expiry.$date.$numberLong)) {
|
||||||
|
// Remove expired offers
|
||||||
|
for (let i = 0; i != processed.ItemManifest.length; ) {
|
||||||
|
if (Date.now() >= parseInt(processed.ItemManifest[i].Expiry.$date.$numberLong)) {
|
||||||
|
processed.ItemManifest.splice(i, 1);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
items.push({
|
|
||||||
StoreItem: rawItem.storeItem,
|
// Add new offers
|
||||||
ItemPrices: rawItem.itemPrices!.map(item => ({ ...item, ProductCategory: "MiscItems" })),
|
const vendorSeed = parseInt(vendorInfo._id.$oid.substring(16), 16);
|
||||||
Bin: "BIN_" + rawItem.bin,
|
const cycleOffset = vendorInfo.cycleOffset ?? 1734307200_000;
|
||||||
QuantityMultiplier: 1,
|
const cycleDuration = vendorInfo.cycleDuration;
|
||||||
Expiry: { $date: { $numberLong: cycleEnd.toString() } },
|
const cycleIndex = Math.trunc((Date.now() - cycleOffset) / cycleDuration);
|
||||||
AllowMultipurchase: false,
|
const rng = new CRng(mixSeeds(vendorSeed, cycleIndex));
|
||||||
LocTagRandSeed: (BigInt(rng.randomInt(0, 0xffffffff)) << 32n) | BigInt(rng.randomInt(0, 0xffffffff)),
|
const manifest = ExportVendors[vendorInfo.TypeName];
|
||||||
Id: { $oid: "67e9da12793a120d" + rng.randomInt(0, 0xffffffff).toString(16).padStart(8, "0") }
|
const offersToAdd = [];
|
||||||
});
|
if (manifest.numItems && !manifest.isOneBinPerCycle) {
|
||||||
|
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
|
||||||
|
while (processed.ItemManifest.length + offersToAdd.length < numItemsTarget) {
|
||||||
|
// TODO: Consider per-bin item limits
|
||||||
|
// TODO: Consider item probability weightings
|
||||||
|
offersToAdd.push(rng.randomElement(manifest.items)!);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let binThisCycle;
|
||||||
|
if (manifest.isOneBinPerCycle) {
|
||||||
|
binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
|
||||||
|
}
|
||||||
|
for (const rawItem of manifest.items) {
|
||||||
|
if (!manifest.isOneBinPerCycle || rawItem.bin == binThisCycle) {
|
||||||
|
offersToAdd.push(rawItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For most vendors, the offers seem to roughly be in reverse order from the manifest. Coda weapons are an odd exception.
|
||||||
|
if (!manifest.isOneBinPerCycle) {
|
||||||
|
offersToAdd.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cycleStart = cycleOffset + cycleIndex * cycleDuration;
|
||||||
|
for (const rawItem of offersToAdd) {
|
||||||
|
const durationHoursRange = toRange(rawItem.durationHours);
|
||||||
|
const expiry =
|
||||||
|
cycleStart +
|
||||||
|
rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour;
|
||||||
|
const item: IItemManifest = {
|
||||||
|
StoreItem: rawItem.storeItem,
|
||||||
|
ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
|
||||||
|
Bin: "BIN_" + rawItem.bin,
|
||||||
|
QuantityMultiplier: 1,
|
||||||
|
Expiry: { $date: { $numberLong: expiry.toString() } },
|
||||||
|
AllowMultipurchase: false,
|
||||||
|
Id: {
|
||||||
|
$oid:
|
||||||
|
((cycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
|
||||||
|
vendorInfo._id.$oid.substring(8, 16) +
|
||||||
|
rng.randomInt(0, 0xffff).toString(16).padStart(4, "0") +
|
||||||
|
rng.randomInt(0, 0xffff).toString(16).padStart(4, "0")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (rawItem.numRandomItemPrices) {
|
||||||
|
item.ItemPrices = [];
|
||||||
|
for (let i = 0; i != rawItem.numRandomItemPrices; ++i) {
|
||||||
|
let itemPrice: { type: string; count: IRange };
|
||||||
|
do {
|
||||||
|
itemPrice = rng.randomElement(manifest.randomItemPricesPerBin![rawItem.bin])!;
|
||||||
|
} while (item.ItemPrices.find(x => x.ItemType == itemPrice.type));
|
||||||
|
item.ItemPrices.push({
|
||||||
|
ItemType: itemPrice.type,
|
||||||
|
ItemCount: rng.randomInt(itemPrice.count.minValue, itemPrice.count.maxValue),
|
||||||
|
ProductCategory: "MiscItems"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rawItem.credits) {
|
||||||
|
const value =
|
||||||
|
typeof rawItem.credits == "number"
|
||||||
|
? rawItem.credits
|
||||||
|
: rng.randomInt(
|
||||||
|
rawItem.credits.minValue / rawItem.credits.step,
|
||||||
|
rawItem.credits.maxValue / rawItem.credits.step
|
||||||
|
) * rawItem.credits.step;
|
||||||
|
item.RegularPrice = [value, value];
|
||||||
|
}
|
||||||
|
if (rawItem.platinum) {
|
||||||
|
const value =
|
||||||
|
typeof rawItem.platinum == "number"
|
||||||
|
? rawItem.platinum
|
||||||
|
: rng.randomInt(rawItem.platinum.minValue, rawItem.platinum.maxValue);
|
||||||
|
item.PremiumPrice = [value, value];
|
||||||
|
}
|
||||||
|
if (vendorInfo.RandomSeedType) {
|
||||||
|
item.LocTagRandSeed = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff);
|
||||||
|
if (vendorInfo.RandomSeedType == "VRST_WEAPON") {
|
||||||
|
const highDword = (rng.randomInt(0, 0xffff) << 16) | rng.randomInt(0, 0xffff);
|
||||||
|
item.LocTagRandSeed = (BigInt(highDword) << 32n) | (BigInt(item.LocTagRandSeed) & 0xffffffffn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processed.ItemManifest.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update vendor expiry
|
||||||
|
let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER;
|
||||||
|
for (const offer of processed.ItemManifest) {
|
||||||
|
const offerExpiry = parseInt(offer.Expiry.$date.$numberLong);
|
||||||
|
if (soonestOfferExpiry > offerExpiry) {
|
||||||
|
soonestOfferExpiry = offerExpiry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processed.Expiry.$date.$numberLong = soonestOfferExpiry.toString();
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
VendorInfo: {
|
VendorInfo: processed
|
||||||
_id: { $oid: "67dadc30e4b6e0e5979c8d84" },
|
|
||||||
TypeName: "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest",
|
|
||||||
ItemManifest: items,
|
|
||||||
PropertyTextHash: "77093DD05A8561A022DEC9A4B9BB4A56",
|
|
||||||
RandomSeedType: "VRST_WEAPON",
|
|
||||||
RequiredGoalTag: "",
|
|
||||||
WeaponUpgradeValueAttenuationExponent: 2.25,
|
|
||||||
Expiry: { $date: { $numberLong: cycleEnd.toString() } }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
|
import { Stats, TStatsDatabaseDocument } from "@/src/models/statsModel";
|
||||||
import {
|
import {
|
||||||
IEnemy,
|
|
||||||
IStatsAdd,
|
IStatsAdd,
|
||||||
IStatsMax,
|
IStatsMax,
|
||||||
IStatsSet,
|
IStatsSet,
|
||||||
@ -137,34 +136,34 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
|
|||||||
case "HEADSHOT":
|
case "HEADSHOT":
|
||||||
case "KILL_ASSIST": {
|
case "KILL_ASSIST": {
|
||||||
playerStats.Enemies ??= [];
|
playerStats.Enemies ??= [];
|
||||||
const enemyStatKey = {
|
const enemyStatKey = (
|
||||||
KILL_ENEMY: "kills",
|
{
|
||||||
EXECUTE_ENEMY: "executions",
|
KILL_ENEMY: "kills",
|
||||||
HEADSHOT: "headshots",
|
EXECUTE_ENEMY: "executions",
|
||||||
KILL_ASSIST: "assists"
|
HEADSHOT: "headshots",
|
||||||
}[category] as "kills" | "executions" | "headshots" | "assists";
|
KILL_ASSIST: "assists"
|
||||||
|
} as const
|
||||||
|
)[category];
|
||||||
|
|
||||||
for (const [type, count] of Object.entries(data as IUploadEntry)) {
|
for (const [type, count] of Object.entries(data as IUploadEntry)) {
|
||||||
const enemy = playerStats.Enemies.find(element => element.type === type);
|
let enemy = playerStats.Enemies.find(element => element.type === type);
|
||||||
if (enemy) {
|
if (!enemy) {
|
||||||
if (category === "KILL_ENEMY") {
|
enemy = { type: type };
|
||||||
enemy.kills ??= 0;
|
playerStats.Enemies.push(enemy);
|
||||||
const captureCount = (actionData as IStatsAdd)["CAPTURE_ENEMY"]?.[type];
|
}
|
||||||
if (captureCount) {
|
if (category === "KILL_ENEMY") {
|
||||||
enemy.kills += Math.max(count - captureCount, 0);
|
enemy.kills ??= 0;
|
||||||
enemy.captures ??= 0;
|
const captureCount = (actionData as IStatsAdd)["CAPTURE_ENEMY"]?.[type];
|
||||||
enemy.captures += captureCount;
|
if (captureCount) {
|
||||||
} else {
|
enemy.kills += Math.max(count - captureCount, 0);
|
||||||
enemy.kills += count;
|
enemy.captures ??= 0;
|
||||||
}
|
enemy.captures += captureCount;
|
||||||
} else {
|
} else {
|
||||||
enemy[enemyStatKey] ??= 0;
|
enemy.kills += count;
|
||||||
enemy[enemyStatKey] += count;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const newEnemy: IEnemy = { type: type };
|
enemy[enemyStatKey] ??= 0;
|
||||||
newEnemy[enemyStatKey] = count;
|
enemy[enemyStatKey] += count;
|
||||||
playerStats.Enemies.push(newEnemy);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -378,7 +377,8 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
|
|||||||
category,
|
category,
|
||||||
accountOwnerId,
|
accountOwnerId,
|
||||||
payload.displayName,
|
payload.displayName,
|
||||||
data as number
|
data as number,
|
||||||
|
payload.guildId
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
1244
src/services/worldStateService.ts
Normal file
1244
src/services/worldStateService.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,7 @@ export interface IGuildClient {
|
|||||||
Members: IGuildMemberClient[];
|
Members: IGuildMemberClient[];
|
||||||
Ranks: IGuildRank[];
|
Ranks: IGuildRank[];
|
||||||
Tier: number;
|
Tier: number;
|
||||||
|
Emblem?: boolean;
|
||||||
Vault: IGuildVault;
|
Vault: IGuildVault;
|
||||||
ActiveDojoColorResearch: string;
|
ActiveDojoColorResearch: string;
|
||||||
Class: number;
|
Class: number;
|
||||||
@ -44,6 +45,7 @@ export interface IGuildDatabase {
|
|||||||
VaultMiscItems?: IMiscItem[];
|
VaultMiscItems?: IMiscItem[];
|
||||||
VaultShipDecorations?: ITypeCount[];
|
VaultShipDecorations?: ITypeCount[];
|
||||||
VaultFusionTreasures?: IFusionTreasure[];
|
VaultFusionTreasures?: IFusionTreasure[];
|
||||||
|
VaultDecoRecipes?: ITypeCount[];
|
||||||
|
|
||||||
TechProjects?: ITechProjectDatabase[];
|
TechProjects?: ITechProjectDatabase[];
|
||||||
ActiveDojoColorResearch: string;
|
ActiveDojoColorResearch: string;
|
||||||
@ -102,12 +104,12 @@ export interface IGuildMemberDatabase {
|
|||||||
ShipDecorationsContributed?: ITypeCount[];
|
ShipDecorationsContributed?: ITypeCount[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFriendInfo {
|
export interface IFriendInfo {
|
||||||
_id: IOid;
|
_id: IOid;
|
||||||
DisplayName?: string;
|
DisplayName?: string;
|
||||||
PlatformNames?: string[];
|
PlatformNames?: string[];
|
||||||
PlatformAccountId?: string;
|
PlatformAccountId?: string;
|
||||||
Status: number;
|
Status?: number;
|
||||||
ActiveAvatarImageType?: string;
|
ActiveAvatarImageType?: string;
|
||||||
LastLogin?: IMongoDate;
|
LastLogin?: IMongoDate;
|
||||||
PlayerLevel?: number;
|
PlayerLevel?: number;
|
||||||
@ -190,6 +192,7 @@ export interface IDojoComponentDatabase
|
|||||||
"id" | "SortId" | "pi" | "CompletionTime" | "DestructionTime" | "Decos" | "PaintBot"
|
"id" | "SortId" | "pi" | "CompletionTime" | "DestructionTime" | "Decos" | "PaintBot"
|
||||||
> {
|
> {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
|
SortId?: Types.ObjectId;
|
||||||
pi?: Types.ObjectId;
|
pi?: Types.ObjectId;
|
||||||
CompletionTime?: Date;
|
CompletionTime?: Date;
|
||||||
CompletionLogPending?: boolean;
|
CompletionLogPending?: boolean;
|
||||||
@ -204,6 +207,7 @@ export interface IDojoDecoClient {
|
|||||||
Type: string;
|
Type: string;
|
||||||
Pos: number[];
|
Pos: number[];
|
||||||
Rot: number[];
|
Rot: number[];
|
||||||
|
Scale?: number;
|
||||||
Name?: string; // for teleporters
|
Name?: string; // for teleporters
|
||||||
Sockets?: number;
|
Sockets?: number;
|
||||||
RegularCredits?: number;
|
RegularCredits?: number;
|
||||||
|
@ -142,6 +142,7 @@ export interface IEquipmentDatabase {
|
|||||||
RailjackImage?: IFlavourItem;
|
RailjackImage?: IFlavourItem;
|
||||||
CrewMembers?: ICrewShipMembersDatabase;
|
CrewMembers?: ICrewShipMembersDatabase;
|
||||||
Details?: IKubrowPetDetailsDatabase;
|
Details?: IKubrowPetDetailsDatabase;
|
||||||
|
Favorite?: boolean;
|
||||||
IsNew?: boolean;
|
IsNew?: boolean;
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user