diff --git a/.eslintrc b/.eslintrc index 70cbe0f1..3f44db43 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,14 +4,13 @@ "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking" ], - "plugins": ["@typescript-eslint"], + "plugins": ["@typescript-eslint", "prettier"], "env": { "browser": true, "es6": true, "node": true }, "rules": { - "@typescript-eslint/semi": "warn", "@typescript-eslint/explicit-function-return-type": "warn", "@typescript-eslint/explicit-module-boundary-types": "warn", "@typescript-eslint/restrict-template-expressions": "warn", @@ -25,7 +24,9 @@ "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/no-loss-of-precision": "warn", "no-case-declarations": "warn", - "no-mixed-spaces-and-tabs": "warn" + "prettier/prettier": "error", + "@typescript-eslint/semi": "error", + "no-mixed-spaces-and-tabs": "error" }, "parser": "@typescript-eslint/parser", "parserOptions": { diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0af70682..3aa07742 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,15 +5,16 @@ on: jobs: build: runs-on: ubuntu-latest - permissions: - contents: write + strategy: + matrix: + version: [18, 20, 22] steps: - name: Checkout uses: actions/checkout@v4.1.2 - name: Setup Node.js environment uses: actions/setup-node@v4.0.2 with: - node-version: "20.x" + node-version: ${{ matrix.version }} - run: npm ci - run: cp config.json.example config.json - run: echo '{"version":"","buildLabel":"","matchmakingBuildId":""}' > static/data/buildConfig.json diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml deleted file mode 100644 index a2458fd8..00000000 --- a/.github/workflows/prettier.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Prettier -on: [push] -jobs: - format: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout - uses: actions/checkout@v4.1.2 - with: - ref: ${{ github.head_ref }} - - name: Setup Node.js environment - uses: actions/setup-node@v4.0.2 - with: - node-version: "20.x" - - run: npm ci - - run: npm run prettier - - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v5.0.0 - with: - commit_message: Apply prettier changes - branch: ${{ github.head_ref }} diff --git a/config.json.example b/config.json.example index 12843a76..6ae52747 100644 --- a/config.json.example +++ b/config.json.example @@ -11,6 +11,7 @@ "autoCreateAccount": true, "skipStoryModeChoice": true, "skipTutorial": true, + "skipAllDialogue": true, "unlockAllScans": true, "unlockAllMissions": true, "unlockAllQuests": true, diff --git a/package-lock.json b/package-lock.json index 8bceb7d8..cbaf65ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,25 +11,24 @@ "dependencies": { "copyfiles": "^2.4.1", "express": "^5.0.0-beta.3", - "mongoose": "^8.1.1", - "warframe-public-export-plus": "^0.4.0", + "mongoose": "^8.4.4", + "warframe-public-export-plus": "^0.4.1", "warframe-riven-info": "^0.1.0", - "winston": "^3.11.0", - "winston-daily-rotate-file": "^4.7.1" + "winston": "^3.13.0", + "winston-daily-rotate-file": "^5.0.0" }, "devDependencies": { - "@tsconfig/node20": "^1.0.0", "@types/express": "^4.17.20", "@types/morgan": "^1.9.7", - "@typescript-eslint/eslint-plugin": "^7.13.1", - "@typescript-eslint/parser": "^7.13.1", + "@typescript-eslint/eslint-plugin": "^7.14", + "@typescript-eslint/parser": "^7.14", "eslint": "^8.56.0", "eslint-plugin-prettier": "^5.1.3", "morgan": "^1.10.0", "prettier": "^3.2.4", "ts-node-dev": "^2.0.0", "tsconfig-paths": "^4.2.0", - "typescript": "^5.2.2" + "typescript": "^5.5" }, "engines": { "node": ">=18.15.0", @@ -236,9 +235,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", - "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", + "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -314,12 +313,6 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, - "node_modules/@tsconfig/node20": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-1.0.0.tgz", - "integrity": "sha512-AwbXtpWEaRUjbGVwdlusNqwet+jeSk3Nnqf/8+77WJ1/9d6xnqs2QpE9Pdwv8RCoXxtMedWEtlmWY+/irBPcUw==", - "dev": true - }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -439,24 +432,24 @@ "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" }, "node_modules/@types/whatwg-url": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", - "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", "dependencies": { "@types/webidl-conversions": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", - "integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz", + "integrity": "sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/type-utils": "7.13.1", - "@typescript-eslint/utils": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/type-utils": "7.15.0", + "@typescript-eslint/utils": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -480,15 +473,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz", - "integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.15.0.tgz", + "integrity": "sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "debug": "^4.3.4" }, "engines": { @@ -531,13 +524,13 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", - "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", + "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1" + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -548,13 +541,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz", - "integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz", + "integrity": "sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/utils": "7.13.1", + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/utils": "7.15.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -598,9 +591,9 @@ "dev": true }, "node_modules/@typescript-eslint/types": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", - "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", + "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -611,13 +604,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", - "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", + "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -665,9 +658,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -686,15 +679,15 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", - "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", + "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1" + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -708,12 +701,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", - "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", + "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/types": "7.15.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -948,9 +941,9 @@ } }, "node_modules/bson": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz", - "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", "engines": { "node": ">=16.20.1" } @@ -2212,9 +2205,9 @@ } }, "node_modules/kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", "engines": { "node": ">=12.0.0" } @@ -2392,12 +2385,12 @@ } }, "node_modules/mongodb": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", - "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", + "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", "dependencies": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.2.0", + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -2437,26 +2430,26 @@ } }, "node_modules/mongodb-connection-string-url": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", - "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", "dependencies": { "@types/whatwg-url": "^11.0.2", "whatwg-url": "^13.0.0" } }, "node_modules/mongoose": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.1.1.tgz", - "integrity": "sha512-DbLb0NsiEXmaqLOpEz+AtAsgwhRw6f25gwa1dF5R7jj6lS1D8X6uTdhBSC8GDVtOwe5Tfw2EL7nTn6hiJT3Bgg==", + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.4.tgz", + "integrity": "sha512-Nya808odIJoHP4JuJKbWA2eIaerXieu59kE8pQlvJpUBoSKWUyhLji0g1WMVaYXWmzPYXP2Jd6XdR4KJE8RELw==", "dependencies": { - "bson": "^6.2.0", - "kareem": "2.5.1", - "mongodb": "6.3.0", + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "6.6.2", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", - "sift": "16.0.1" + "sift": "17.1.3" }, "engines": { "node": ">=16.20.1" @@ -2581,9 +2574,9 @@ } }, "node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "engines": { "node": ">= 6" } @@ -3168,9 +3161,9 @@ } }, "node_modules/sift": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", - "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -3604,9 +3597,9 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3669,9 +3662,9 @@ } }, "node_modules/warframe-public-export-plus": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.4.0.tgz", - "integrity": "sha512-8wOkh9dET4IHmHDSZ8g8RW0GlfEevHnBwEETAqy3jRhwssyF0TgQsOOpJVuhcPKedCYeudR92HJ3JoXoiTCr6A==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.4.1.tgz", + "integrity": "sha512-5SwnT/K/rMI0zJpdodzeEPlO/UnMlHiKv8NZGH647/5u52LZf8xfOpJHP4/yr/anjVVzDQJwY5K3CmbX0uMQdw==" }, "node_modules/warframe-riven-info": { "version": "0.1.0", @@ -3714,9 +3707,9 @@ } }, "node_modules/winston": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", - "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", @@ -3728,21 +3721,21 @@ "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" + "winston-transport": "^4.7.0" }, "engines": { "node": ">= 12.0.0" } }, "node_modules/winston-daily-rotate-file": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz", - "integrity": "sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz", + "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==", "dependencies": { "file-stream-rotator": "^0.6.1", - "object-hash": "^2.0.1", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" + "object-hash": "^3.0.0", + "triple-beam": "^1.4.1", + "winston-transport": "^4.7.0" }, "engines": { "node": ">=8" @@ -3752,9 +3745,9 @@ } }, "node_modules/winston-transport": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz", - "integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", "dependencies": { "logform": "^2.3.2", "readable-stream": "^3.6.0", diff --git a/package.json b/package.json index 522d03c4..14647f4c 100644 --- a/package.json +++ b/package.json @@ -15,25 +15,24 @@ "dependencies": { "copyfiles": "^2.4.1", "express": "^5.0.0-beta.3", - "mongoose": "^8.1.1", - "warframe-public-export-plus": "^0.4.0", + "mongoose": "^8.4.4", + "warframe-public-export-plus": "^0.4.1", "warframe-riven-info": "^0.1.0", - "winston": "^3.11.0", - "winston-daily-rotate-file": "^4.7.1" + "winston": "^3.13.0", + "winston-daily-rotate-file": "^5.0.0" }, "devDependencies": { - "@tsconfig/node20": "^1.0.0", "@types/express": "^4.17.20", "@types/morgan": "^1.9.7", - "@typescript-eslint/eslint-plugin": "^7.13.1", - "@typescript-eslint/parser": "^7.13.1", + "@typescript-eslint/eslint-plugin": "^7.14", + "@typescript-eslint/parser": "^7.14", "eslint": "^8.56.0", "eslint-plugin-prettier": "^5.1.3", "morgan": "^1.10.0", "prettier": "^3.2.4", "ts-node-dev": "^2.0.0", "tsconfig-paths": "^4.2.0", - "typescript": "^5.2.2" + "typescript": "^5.5" }, "engines": { "node": ">=18.15.0", diff --git a/src/controllers/api/getVendorInfoController.ts b/src/controllers/api/getVendorInfoController.ts index cd442318..aa831028 100644 --- a/src/controllers/api/getVendorInfoController.ts +++ b/src/controllers/api/getVendorInfoController.ts @@ -1,7 +1,23 @@ import { RequestHandler } from "express"; -import getVendorInfoResponse from "@/static/fixed_responses/getVendorInfo.json"; +import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json"; +import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json"; +import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json"; export const getVendorInfoController: RequestHandler = (req, res) => { - console.assert(req.query.vendor == "/Lotus/Types/Game/VendorManifests/Ostron/MaskSalesmanManifest"); - res.json(getVendorInfoResponse); + switch (req.query.vendor as string) { + case "/Lotus/Types/Game/VendorManifests/Zariman/ArchimedeanVendorManifest": + res.json(ArchimedeanVendorManifest); + break; + + case "/Lotus/Types/Game/VendorManifests/Ostron/MaskSalesmanManifest": + res.json(MaskSalesmanManifest); + break; + + case "/Lotus/Types/Game/VendorManifests/Zariman/ZarimanCommisionsManifestArchimedean": + res.json(ZarimanCommisionsManifestArchimedean); + break; + + default: + throw new Error(`Unknown vendor: ${req.query.vendor}`); + } }; diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts index a37a8a90..15bd28d8 100644 --- a/src/controllers/api/inventoryController.ts +++ b/src/controllers/api/inventoryController.ts @@ -3,9 +3,10 @@ import { getAccountIdForRequest } from "@/src/services/loginService"; import { toInventoryResponse } from "@/src/helpers/inventoryHelpers"; import { Inventory } from "@/src/models/inventoryModels/inventoryModel"; import { config } from "@/src/services/configService"; +import allDialogue from "@/static/fixed_responses/allDialogue.json"; import allMissions from "@/static/fixed_responses/allMissions.json"; import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes"; -import { IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes"; +import { IInventoryDatabase, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes"; import { IPolarity, ArtifactPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { ExportCustoms, ExportFlavour, ExportKeys, ExportResources } from "warframe-public-export-plus"; @@ -20,9 +21,7 @@ const inventoryController: RequestHandler = async (request, response) => { } const inventory = await Inventory.findOne({ accountOwnerId: accountId }) - .populate<{ - LoadOutPresets: ILoadoutDatabase; - }>("LoadOutPresets") + .populate<{ LoadOutPresets: ILoadoutDatabase }>("LoadOutPresets") .populate<{ Ships: IShipInventory }>("Ships", "-ShipInteriorColors"); if (!inventory) { @@ -31,7 +30,7 @@ const inventoryController: RequestHandler = async (request, response) => { } //TODO: make a function that converts from database representation to client - const inventoryJSON = inventory.toJSON(); + const inventoryJSON: IInventoryDatabase = inventory.toJSON(); console.log(inventoryJSON.Ships); const inventoryResponse = toInventoryResponse(inventoryJSON); @@ -43,9 +42,21 @@ const inventoryController: RequestHandler = async (request, response) => { inventoryResponse.PremiumCredits = 999999999; } + if (config.skipAllDialogue) { + inventoryResponse.TauntHistory = [ + { + node: "TreasureTutorial", + state: "TS_COMPLETED" + } + ]; + for (const str of allDialogue) { + addString(inventoryResponse.NodeIntrosCompleted, str); + } + } + if (config.unlockAllMissions) { inventoryResponse.Missions = allMissions; - inventoryResponse.NodeIntrosCompleted.push("TeshinHardModeUnlocked"); + addString(inventoryResponse.NodeIntrosCompleted, "TeshinHardModeUnlocked"); } if (config.unlockAllQuests) { @@ -73,7 +84,7 @@ const inventoryController: RequestHandler = async (request, response) => { inventoryResponse.ArchwingEnabled = true; // Skip "Watch The Maker" - inventoryResponse.NodeIntrosCompleted.push("/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level"); + addString(inventoryResponse.NodeIntrosCompleted, "/Lotus/Levels/Cinematics/NewWarIntro/NewWarStageTwo.level"); } if (config.unlockAllShipDecorations) { @@ -142,6 +153,12 @@ const inventoryController: RequestHandler = async (request, response) => { response.json(inventoryResponse); }; +const addString = (arr: string[], str: string): void => { + if (!arr.find(x => x == str)) { + arr.push(str); + } +}; + const getExpRequiredForMr = (rank: number): number => { if (rank <= 30) { return 2500 * rank * rank; diff --git a/src/controllers/api/projectionManagerController.ts b/src/controllers/api/projectionManagerController.ts new file mode 100644 index 00000000..0872cde0 --- /dev/null +++ b/src/controllers/api/projectionManagerController.ts @@ -0,0 +1,64 @@ +import { RequestHandler } from "express"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { addMiscItems, getInventory } from "@/src/services/inventoryService"; +import { ExportRelics, IRelic } from "warframe-public-export-plus"; + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +export const projectionManagerController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const request = JSON.parse(String(req.body)) as IProjectionUpgradeRequest; + const [era, category, currentQuality] = parseProjection(request.projectionType); + const upgradeCost = (request.qualityTag - qualityKeywordToNumber[currentQuality]) * 25; + const newProjectionType = findProjection(era, category, qualityNumberToKeyword[request.qualityTag]); + addMiscItems(inventory, [ + { + ItemType: request.projectionType, + ItemCount: -1 + }, + { + ItemType: newProjectionType, + ItemCount: 1 + }, + { + ItemType: "/Lotus/Types/Items/MiscItems/VoidTearDrop", + ItemCount: -upgradeCost + } + ]); + await inventory.save(); + res.json({ + prevProjection: request.projectionType, + upgradedProjection: newProjectionType, + upgradeCost: upgradeCost + }); +}; + +interface IProjectionUpgradeRequest { + projectionType: string; + qualityTag: number; +} + +type VoidProjectionQuality = "VPQ_BRONZE" | "VPQ_SILVER" | "VPQ_GOLD" | "VPQ_PLATINUM"; + +const qualityNumberToKeyword: VoidProjectionQuality[] = ["VPQ_BRONZE", "VPQ_SILVER", "VPQ_GOLD", "VPQ_PLATINUM"]; +const qualityKeywordToNumber: Record = { + VPQ_BRONZE: 0, + VPQ_SILVER: 1, + VPQ_GOLD: 2, + VPQ_PLATINUM: 3 +}; + +// e.g. "/Lotus/Types/Game/Projections/T2VoidProjectionProteaPrimeDBronze" -> ["Lith", "W5", "VPQ_BRONZE"] +const parseProjection = (typeName: string): [string, string, VoidProjectionQuality] => { + const relic: IRelic | undefined = ExportRelics[typeName]; + if (!relic) { + throw new Error(`Unknown projection ${typeName}`); + } + return [relic.era, relic.category, relic.quality]; +}; + +const findProjection = (era: string, category: string, quality: VoidProjectionQuality): string => { + return Object.entries(ExportRelics).find( + ([_, relic]) => relic.era == era && relic.category == category && relic.quality == quality + )![0]; +}; diff --git a/src/controllers/api/tauntHistoryController.ts b/src/controllers/api/tauntHistoryController.ts new file mode 100644 index 00000000..6da5cb2c --- /dev/null +++ b/src/controllers/api/tauntHistoryController.ts @@ -0,0 +1,22 @@ +import { RequestHandler } from "express"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { getInventory } from "@/src/services/inventoryService"; +import { ITaunt } from "@/src/types/inventoryTypes/inventoryTypes"; +import { logger } from "@/src/utils/logger"; + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +export const tauntHistoryController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const clientTaunt = JSON.parse(String(req.body)) as ITaunt; + logger.debug(`updating taunt ${clientTaunt.node} to state ${clientTaunt.state}`); + inventory.TauntHistory ??= []; + const taunt = inventory.TauntHistory.find(x => x.node == clientTaunt.node); + if (taunt) { + taunt.state = clientTaunt.state; + } else { + inventory.TauntHistory.push(clientTaunt); + } + await inventory.save(); + res.end(); +}; diff --git a/src/controllers/api/upgradesController.ts b/src/controllers/api/upgradesController.ts index 8fdec7e7..c0004783 100644 --- a/src/controllers/api/upgradesController.ts +++ b/src/controllers/api/upgradesController.ts @@ -64,6 +64,7 @@ export const upgradesController: RequestHandler = async (req, res) => { case "/Lotus/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker": case "/Lotus/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker": case "/Lotus/Types/Items/MiscItems/WeaponMeleeArcaneUnlocker": + case "/Lotus/Types/Items/MiscItems/WeaponAmpArcaneUnlocker": for (const item of inventory[payload.ItemCategory]) { if (item._id.toString() == payload.ItemId.$oid) { item.Features ??= 0; diff --git a/src/controllers/custom/pushArchonCrystalUpgradeController.ts b/src/controllers/custom/pushArchonCrystalUpgradeController.ts index cbe1023d..c12ea584 100644 --- a/src/controllers/custom/pushArchonCrystalUpgradeController.ts +++ b/src/controllers/custom/pushArchonCrystalUpgradeController.ts @@ -10,11 +10,13 @@ export const pushArchonCrystalUpgradeController: RequestHandler = async (req, re if (suit) { suit.ArchonCrystalUpgrades ??= []; const count = (req.query.count as number | undefined) ?? 1; - for (let i = 0; i != count; ++i) { - suit.ArchonCrystalUpgrades.push({ UpgradeType: req.query.type as string }); + if (count >= 1 && count <= 10000) { + for (let i = 0; i != count; ++i) { + suit.ArchonCrystalUpgrades.push({ UpgradeType: req.query.type as string }); + } + await inventory.save(); + res.end(); } - await inventory.save(); - res.end(); } res.status(400).end(); }; diff --git a/src/middleware/middleware.ts b/src/middleware/middleware.ts index 0a406672..68e60191 100644 --- a/src/middleware/middleware.ts +++ b/src/middleware/middleware.ts @@ -3,6 +3,9 @@ import { /*NextFunction,*/ Request, Response } from "express"; const unknownEndpointHandler = (request: Request, response: Response) => { logger.error(`unknown endpoint ${request.method} ${request.path}`); + if (request.body) { + logger.debug(`data provided to ${request.path}: ${String(request.body)}`); + } response.status(404).json({ error: "endpoint was not found" }); }; diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index 487600de..aee6aef2 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -31,7 +31,7 @@ import { IFusionTreasure, ISpectreLoadout, IWeaponSkinDatabase, - ITauntHistory, + ITaunt, IPeriodicMissionCompletionDatabase, IPeriodicMissionCompletionResponse, ILoreFragmentScan, @@ -533,7 +533,7 @@ weaponSkinsSchema.set("toJSON", { } }); -const tauntHistorySchema = new Schema( +const tauntSchema = new Schema( { node: String, state: String @@ -772,8 +772,8 @@ const inventorySchema = new Schema( //Ayatan Item FusionTreasures: [fusionTreasuresSchema], - //"node": "TreasureTutorial", "state": "TS_COMPLETED" - TauntHistory: [tauntHistorySchema], + //only used for Maroo apparently - { "node": "TreasureTutorial", "state": "TS_COMPLETED" } + TauntHistory: { type: [tauntSchema], default: undefined }, //noShow2FA,VisitPrimeVault etc WebFlags: Schema.Types.Mixed, diff --git a/src/routes/api.ts b/src/routes/api.ts index 4e7b7dc5..aed616a5 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -24,6 +24,7 @@ import { getShipController } from "@/src/controllers/api/getShipController"; import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController"; import { giveKeyChainTriggeredItemsController } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; import { giveKeyChainTriggeredMessageController } from "@/src/controllers/api/giveKeyChainTriggeredMessageController"; +import { gildWeaponController } from "@/src/controllers/api/gildWeaponController"; import { guildTechController } from "@/src/controllers/api/guildTechController"; import { hostSessionController } from "@/src/controllers/api/hostSessionController"; import { hubController } from "@/src/controllers/api/hubController"; @@ -41,6 +42,7 @@ import { missionInventoryUpdateController } from "@/src/controllers/api/missionI import { modularWeaponCraftingController } from "@/src/controllers/api/modularWeaponCraftingController"; import { modularWeaponSaleController } from "@/src/controllers/api/modularWeaponSaleController"; import { nameWeaponController } from "@/src/controllers/api/nameWeaponController"; +import { projectionManagerController } from "../controllers/api/projectionManagerController"; import { purchaseController } from "@/src/controllers/api/purchaseController"; import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController"; import { rerollRandomModController } from "@/src/controllers/api/rerollRandomModController"; @@ -57,7 +59,8 @@ import { startDojoRecipeController } from "@/src/controllers/api/startDojoRecipe import { startRecipeController } from "@/src/controllers/api/startRecipeController"; import { stepSequencersController } from "@/src/controllers/api/stepSequencersController"; import { surveysController } from "@/src/controllers/api/surveysController"; -import { syndicateSacrificeController } from "@/src/controllers/api/syndicateSacrificeController"; +import { syndicateSacrificeController } from "../controllers/api/syndicateSacrificeController"; +import { tauntHistoryController } from "@/src/controllers/api/tauntHistoryController"; import { trainingResultController } from "@/src/controllers/api/trainingResultController"; import { unlockShipFeatureController } from "@/src/controllers/api/unlockShipFeatureController"; import { updateChallengeProgressController } from "@/src/controllers/api/updateChallengeProgressController"; @@ -65,7 +68,6 @@ import { updateQuestController } from "@/src/controllers/api/updateQuestControll import { updateSessionGetController, updateSessionPostController } from "@/src/controllers/api/updateSessionController"; import { updateThemeController } from "@/src/controllers/api/updateThemeController"; import { upgradesController } from "@/src/controllers/api/upgradesController"; -import { gildWeaponController } from "@/src/controllers/api/gildWeaponController"; const apiRouter = express.Router(); @@ -123,6 +125,7 @@ apiRouter.post("/login.php", loginController); apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController); apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController); apiRouter.post("/nameWeapon.php", nameWeaponController); +apiRouter.post("/projectionManager.php", projectionManagerController); apiRouter.post("/purchase.php", purchaseController); apiRouter.post("/rerollRandomMod.php", rerollRandomModController); apiRouter.post("/saveLoadout.php", saveLoadoutController); @@ -134,6 +137,7 @@ apiRouter.post("/startDojoRecipe.php", startDojoRecipeController); apiRouter.post("/startRecipe.php", startRecipeController); apiRouter.post("/stepSequencers.php", stepSequencersController); apiRouter.post("/syndicateSacrifice.php", syndicateSacrificeController); +apiRouter.post("/tauntHistory.php", tauntHistoryController); apiRouter.post("/trainingResult.php", trainingResultController); apiRouter.post("/unlockShipFeature.php", unlockShipFeatureController); apiRouter.post("/updateChallengeProgress.php", updateChallengeProgressController); diff --git a/src/services/configService.ts b/src/services/configService.ts index 272f50ac..ab16cb7e 100644 --- a/src/services/configService.ts +++ b/src/services/configService.ts @@ -28,6 +28,7 @@ interface IConfig { autoCreateAccount?: boolean; skipStoryModeChoice?: boolean; skipTutorial?: boolean; + skipAllDialogue?: boolean; unlockAllScans?: boolean; unlockAllMissions?: boolean; unlockAllQuests?: boolean; diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 81a35dae..4041347d 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -2,7 +2,7 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel"; import new_inventory from "@/static/fixed_responses/postTutorialInventory.json"; import { config } from "@/src/services/configService"; import { Types } from "mongoose"; -import { SlotNames, IInventoryChanges } from "@/src/types/purchaseTypes"; +import { SlotNames, IInventoryChanges, IBinChanges } from "@/src/types/purchaseTypes"; import { IChallengeProgress, IConsumable, @@ -19,7 +19,8 @@ import { KubrowPetEggItemType, IKubrowPetEgg, TEquipmentKey, - equipmentKeys + equipmentKeys, + IFusionTreasure } from "@/src/types/inventoryTypes/inventoryTypes"; import { IGenericUpdate } from "../types/genericUpdate"; import { @@ -34,6 +35,7 @@ import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndi import { IEquipmentClient, IEquipmentDatabase } from "../types/inventoryTypes/commonInventoryTypes"; import { ExportArcanes, + ExportBoosterPacks, ExportCustoms, ExportFlavour, ExportFusionBundles, @@ -44,6 +46,7 @@ import { } from "warframe-public-export-plus"; import { updateQuestKeys } from "./questService"; import { toOid } from "../helpers/inventoryHelpers"; +import { getRandomWeightedReward } from "./rngService"; export const createInventory = async ( accountOwnerId: Types.ObjectId, @@ -73,6 +76,31 @@ export const createInventory = async ( } }; +export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, delta: IInventoryChanges): void => { + for (const key in delta) { + if (!(key in InventoryChanges)) { + InventoryChanges[key] = delta[key]; + } else if (Array.isArray(delta[key])) { + const left = InventoryChanges[key] as object[]; + const right: object[] = delta[key] as object[]; + for (const item of right) { + left.push(item); + } + } else { + console.assert(key.substring(-3) == "Bin"); + const left = InventoryChanges[key] as IBinChanges; + const right: IBinChanges = delta[key] as IBinChanges; + left.count += right.count; + left.platinum += right.platinum; + left.Slots += right.Slots; + if (right.Extra) { + left.Extra ??= 0; + left.Extra += right.Extra; + } + } + } +}; + export const getInventory = async (accountOwnerId: string) => { const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId }); @@ -228,7 +256,38 @@ export const addItem = async ( } }; } + if (typeName in ExportBoosterPacks) { + const pack = ExportBoosterPacks[typeName]; + const InventoryChanges = {}; + for (const weights of pack.rarityWeightsPerRoll) { + const result = getRandomWeightedReward(pack.components, weights); + if (result) { + logger.debug(`booster pack rolled`, result); + combineInventoryChanges( + InventoryChanges, + (await addItem(accountId, result.type, result.itemCount)).InventoryChanges + ); + } + } + return { InventoryChanges }; + } + if (miscItems.includes(typeName)) { + const miscItemChanges = [ + { + ItemType: typeName, + ItemCount: quantity + } satisfies IMiscItem + ]; + const inventory = await getInventory(accountId); + addMiscItems(inventory, miscItemChanges); + await inventory.save(); + return { + InventoryChanges: { + MiscItems: miscItemChanges + } + }; + } // Path-based duck typing switch (typeName.substring(1).split("/")[1]) { case "Powersuits": @@ -368,8 +427,26 @@ export const addItem = async ( } } } - case "Restoratives": { - // Codex Scanner, Remote Observer, Starburst + case "Game": + if (typeName.substring(1).split("/")[3] == "Projections") { + // Void Relics, e.g. /Lotus/Types/Game/Projections/T2VoidProjectionGaussPrimeDBronze + const inventory = await getInventory(accountId); + const miscItemChanges = [ + { + ItemType: typeName, + ItemCount: quantity + } satisfies IMiscItem + ]; + addMiscItems(inventory, miscItemChanges); + await inventory.save(); + return { + InventoryChanges: { + MiscItems: miscItemChanges + } + }; + } + break; + case "Restoratives": // Codex Scanner, Remote Observer, Starburst const inventory = await getInventory(accountId); const consumablesChanges = [ { @@ -384,26 +461,9 @@ export const addItem = async ( Consumables: consumablesChanges } }; - } } break; } - if (miscItems.includes(typeName)) { - const miscItemChanges = [ - { - ItemType: typeName, - ItemCount: quantity - } satisfies IMiscItem - ]; - const inventory = await getInventory(accountId); - addMiscItems(inventory, miscItemChanges); - await inventory.save(); - return { - InventoryChanges: { - MiscItems: miscItemChanges - } - }; - } const errorMessage = `unable to add item: ${typeName}`; logger.error(errorMessage); throw new Error(errorMessage); @@ -716,6 +776,26 @@ export const addMods = (inventory: IInventoryDatabaseDocument, itemsArray: IRawU }); }; +export const addFusionTreasures = ( + inventory: IInventoryDatabaseDocument, + itemsArray: IFusionTreasure[] | undefined +) => { + const { FusionTreasures } = inventory; + itemsArray?.forEach(({ ItemType, ItemCount, Sockets }) => { + const itemIndex = FusionTreasures.findIndex(i => { + i.ItemType === ItemType; + i.Sockets === Sockets; + }); + + if (itemIndex !== -1) { + FusionTreasures[itemIndex].ItemCount += ItemCount; + inventory.markModified(`FusionTreasures.${itemIndex}.ItemCount`); + } else { + FusionTreasures.push({ ItemCount, ItemType, Sockets }); + } + }); +}; + export const updateChallengeProgress = async (challenges: IUpdateChallengeProgressRequest, accountId: string) => { const inventory = await getInventory(accountId); @@ -779,7 +859,8 @@ export const missionInventoryUpdate = async (data: IMissionInventoryUpdateReques Consumables, Recipes, Missions, - QuestKeys + QuestKeys, + FusionTreasures } = data; const inventory = await getInventory(accountId); @@ -837,6 +918,7 @@ export const missionInventoryUpdate = async (data: IMissionInventoryUpdateReques addConsumables(inventory, Consumables); addRecipes(inventory, Recipes); addChallenges(inventory, ChallengeProgress); + addFusionTreasures(inventory, FusionTreasures); if (Missions) { addMissionComplete(inventory, Missions); } diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index 119e8086..3fbedcb0 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -13,6 +13,7 @@ import { } from "warframe-public-export-plus"; import { IMissionInventoryUpdateRequest } from "../types/requestTypes"; import { logger } from "@/src/utils/logger"; +import { IRngResult, getRandomReward } from "@/src/services/rngService"; // need reverse engineer rewardSeed, otherwise ingame displayed rotation reward will be different than added to db or displayed on mission end const getRewards = ({ @@ -25,7 +26,7 @@ const getRewards = ({ return { InventoryChanges: {}, MissionRewards: [] }; } - const drops: IReward[] = []; + const drops: IRngResult[] = []; if (RewardInfo.node in ExportRegions) { const region = ExportRegions[RewardInfo.node]; const rewardManifests = region.rewardManifests ?? []; @@ -127,21 +128,8 @@ const getRotations = (rotationCount: number): number[] => { return rotatedValues; }; -const getRandomRewardByChance = (data: IReward[]): IReward | undefined => { - if (data.length == 0) return; - - const totalChance = data.reduce((sum, item) => sum + item.probability!, 0); - const randomValue = Math.random() * totalChance; - - let cumulativeChance = 0; - for (const item of data) { - cumulativeChance += item.probability!; - if (randomValue <= cumulativeChance) { - return item; - } - } - - return; +const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => { + return getRandomReward(pool as IRngResult[]); }; const creditBundles: Record = { @@ -157,7 +145,7 @@ const creditBundles: Record = { "/Lotus/StoreItems/Types/StoreItems/CreditBundles/Zariman/TableACreditsUncommon": 30000, "/Lotus/StoreItems/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardOneHard": 105000, "/Lotus/StoreItems/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardTwoHard": 175000, - "/Lotus/StoreItems/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardThreeHard": 25000 + "/Lotus/StoreItems/Types/PickUps/Credits/CorpusArenaCreditRewards/CorpusArenaRewardThreeHard": 250000 }; const fusionBundles: Record = { @@ -167,7 +155,7 @@ const fusionBundles: Record = { }; const formatRewardsToInventoryType = ( - rewards: IReward[] + rewards: IRngResult[] ): { InventoryChanges: IMissionInventoryUpdateRequest; MissionRewards: IMissionRewardResponse[] } => { const InventoryChanges: IMissionInventoryUpdateRequest = {}; const MissionRewards: IMissionRewardResponse[] = []; @@ -211,7 +199,7 @@ const addRewardResponse = ( InventoryChanges[InventoryCategory] = []; } - const existReward = InventoryChanges[InventoryCategory]!.find(item => item.ItemType === ItemType); + const existReward = InventoryChanges[InventoryCategory].find(item => item.ItemType === ItemType); if (existReward) { existReward.ItemCount += ItemCount; const missionReward = MissionRewards.find(missionReward => missionReward.TypeName === ItemType); @@ -219,7 +207,7 @@ const addRewardResponse = ( missionReward.ItemCount += ItemCount; } } else { - InventoryChanges[InventoryCategory]!.push({ ItemType, ItemCount }); + InventoryChanges[InventoryCategory].push({ ItemType, ItemCount }); MissionRewards.push({ ItemCount, TweetText: ItemType, // ensure if/how this even still used, or if it's needed at all diff --git a/src/services/purchaseService.ts b/src/services/purchaseService.ts index add4032c..29bdc218 100644 --- a/src/services/purchaseService.ts +++ b/src/services/purchaseService.ts @@ -1,7 +1,13 @@ import { parseSlotPurchaseName } from "@/src/helpers/purchaseHelpers"; import { getSubstringFromKeyword } from "@/src/helpers/stringHelpers"; -import { addItem, addBooster, updateCurrency, updateSlots } from "@/src/services/inventoryService"; -import { IPurchaseRequest, SlotPurchase, IInventoryChanges, IBinChanges } from "@/src/types/purchaseTypes"; +import { + addItem, + addBooster, + combineInventoryChanges, + updateCurrency, + updateSlots +} from "@/src/services/inventoryService"; +import { IPurchaseRequest, SlotPurchase, IInventoryChanges } from "@/src/types/purchaseTypes"; import { logger } from "@/src/utils/logger"; import { ExportBundles, ExportGear, TRarity } from "warframe-public-export-plus"; @@ -46,31 +52,6 @@ export const handlePurchase = async (purchaseRequest: IPurchaseRequest, accountI return purchaseResponse; }; -const addInventoryChanges = (InventoryChanges: IInventoryChanges, delta: IInventoryChanges): void => { - for (const key in delta) { - if (!(key in InventoryChanges)) { - InventoryChanges[key] = delta[key]; - } else if (Array.isArray(delta[key])) { - const left = InventoryChanges[key] as object[]; - const right = delta[key] as object[]; - for (const item of right) { - left.push(item); - } - } else { - console.assert(key.substring(-3) == "Bin"); - const left = InventoryChanges[key] as IBinChanges; - const right = delta[key] as IBinChanges; - left.count += right.count; - left.platinum += right.platinum; - left.Slots += right.Slots; - if (right.Extra) { - left.Extra ??= 0; - left.Extra += right.Extra; - } - } - } -}; - const handleStoreItemAcquisition = async ( storeItemName: string, accountId: string, @@ -86,7 +67,7 @@ const handleStoreItemAcquisition = async ( const bundle = ExportBundles[storeItemName]; logger.debug("acquiring bundle", bundle); for (const component of bundle.components) { - addInventoryChanges( + combineInventoryChanges( purchaseResponse.InventoryChanges, ( await handleStoreItemAcquisition( diff --git a/src/services/rngService.ts b/src/services/rngService.ts new file mode 100644 index 00000000..fad3a4ff --- /dev/null +++ b/src/services/rngService.ts @@ -0,0 +1,42 @@ +import { TRarity } from "warframe-public-export-plus"; + +export interface IRngResult { + type: string; + itemCount: number; + probability: number; +} + +export const getRandomReward = (pool: IRngResult[]): IRngResult | undefined => { + if (pool.length == 0) return; + + const totalChance = pool.reduce((accum, item) => accum + item.probability, 0); + const randomValue = Math.random() * totalChance; + + let cumulativeChance = 0; + for (const item of pool) { + cumulativeChance += item.probability; + if (randomValue <= cumulativeChance) { + return item; + } + } + throw new Error("What the fuck?"); +}; + +export const getRandomWeightedReward = ( + pool: { Item: string; Rarity: TRarity }[], + weights: Record +): IRngResult | undefined => { + const resultPool: IRngResult[] = []; + const rarityCounts: Record = { COMMON: 0, UNCOMMON: 0, RARE: 0, LEGENDARY: 0 }; + for (const entry of pool) { + ++rarityCounts[entry.Rarity]; + } + for (const entry of pool) { + resultPool.push({ + type: entry.Item, + itemCount: 1, + probability: weights[entry.Rarity] / rarityCounts[entry.Rarity] + }); + } + return getRandomReward(resultPool); +}; diff --git a/src/services/saveLoadoutService.ts b/src/services/saveLoadoutService.ts index 9ff6e523..6cd6d7b2 100644 --- a/src/services/saveLoadoutService.ts +++ b/src/services/saveLoadoutService.ts @@ -167,8 +167,9 @@ export const handleInventoryItemConfigChange = async ( inventory.CurrentLoadOutIds = loadoutIds; break; } - case "EquippedGear": { - inventory.EquippedGear = equipment as string[]; + case "EquippedGear": + case "EquippedEmotes": { + inventory[equipmentName] = equipment as string[]; break; } case "UseAdultOperatorLoadout": { diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 3c049fa9..e0cdff3c 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -177,7 +177,7 @@ export interface IInventoryResponse { CompletedAlerts: string[]; Consumables: IConsumable[]; LevelKeys: IConsumable[]; - TauntHistory: ITauntHistory[]; + TauntHistory?: ITaunt[]; StoryModeChoice: string; PeriodicMissionCompletions: IPeriodicMissionCompletionDatabase[]; KubrowPetEggs: IKubrowPetEgg[]; @@ -888,9 +888,9 @@ export interface INotePacks { PERCUSSION: string; } -export interface ITauntHistory { +export interface ITaunt { node: string; - state: string; + state: "TS_UNLOCKED" | "TS_COMPLETED"; } export interface IWeaponSkinDatabase { diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts index 0dd0d414..237699fc 100644 --- a/src/types/requestTypes.ts +++ b/src/types/requestTypes.ts @@ -11,9 +11,10 @@ import { IMission, IRawUpgrade, ISeasonChallenge, - TEquipmentKey, + TSolarMapRegion, IQuestKeyDatabase, - TSolarMapRegion + TEquipmentKey, + IFusionTreasure } from "./inventoryTypes/inventoryTypes"; export interface IArtifactsRequest { @@ -62,6 +63,7 @@ export interface IMissionInventoryUpdateRequest { RawUpgrades?: IRawUpgrade[]; MiscItems?: IMiscItem[]; Consumables?: IConsumable[]; + FusionTreasures?: IFusionTreasure[]; Recipes?: IConsumable[]; RegularCredits?: number; ChallengeProgress?: IChallengeProgress[]; diff --git a/src/types/saveLoadoutTypes.ts b/src/types/saveLoadoutTypes.ts index 9121aadf..731e4fb0 100644 --- a/src/types/saveLoadoutTypes.ts +++ b/src/types/saveLoadoutTypes.ts @@ -33,6 +33,7 @@ export interface ISaveLoadoutRequest { CurrentLoadOutIds: IOid[]; ValidNewLoadoutId: string; EquippedGear: string[]; + EquippedEmotes: string[]; UseAdultOperatorLoadout: boolean; } diff --git a/static/fixed_responses/allDialogue.json b/static/fixed_responses/allDialogue.json new file mode 100644 index 00000000..a4ee727a --- /dev/null +++ b/static/fixed_responses/allDialogue.json @@ -0,0 +1,139 @@ +[ + "SolarisUnitedHub1", + "/Lotus/Language/SolarisVenus/FishmongerName", + "/Lotus/Language/SolarisVenus/ProspectorName", + "/Lotus/Language/SolarisVenus/StockbrokerName", + "/Lotus/Language/SolarisVenus/WeaponsmithShopName", + "/Lotus/Language/SolarisVenus/LegsShopName", + "/Lotus/Language/Actions/KDriveVendor", + "/Lotus/Language/SolarisVenus/SolarisUnitedContactName", + "SaturnWolf1", + "SaturnWolf2", + "SaturnWolf3", + "SaturnWolf4", + "SaturnWolf5", + "ConclaveSyndicate", + "ArbitersSyndicate", + "LibrarySyndicate", + "RedVeilSyndicate", + "PerrinSyndicate", + "CephalonSudaSyndicate", + "NewLokaSyndicate", + "SteelMeridianSyndicate", + "CetusSyndicate", + "QuillsSyndicate", + "VentKidsSyndicate", + "SolarisSyndicate", + "VoxSyndicate", + "RadioLegionSyndicate", + "EventSyndicate", + "TheEmissary1", + "TheEmissary2", + "TheEmissary3", + "TheEmissary4", + "LeverianIntro", + "TheEmissary5", + "RailjackMultiToolIntro", + "EnterRailjackOnceOnly", + "ScenarioEventHub5", + "Glassmaker1", + "/Lotus/Types/Items/Glassmaker/CetusBurnedNoteCEvidence", + "/Lotus/Types/Items/Glassmaker/CetusWeaponBEvidence", + "/Lotus/Types/Items/Glassmaker/CetusDatapadOneBEvidence", + "/Lotus/Types/Items/Glassmaker/CetusEyeBEvidence", + "/Lotus/Types/Items/Glassmaker/CetusDatapadTwoAEvidence", + "/Lotus/Types/Items/Glassmaker/WeaveEvidencePartOne", + "Glassmaker2", + "/Lotus/Types/Items/Glassmaker/CorpusJournalBEvidence", + "/Lotus/Types/Items/Glassmaker/CorpusCatalogRobotsEvidence", + "/Lotus/Types/Items/Glassmaker/CorpusDatapadEvidenceC", + "/Lotus/Types/Items/Glassmaker/CorpusHelmetEngineerEvidence", + "/Lotus/Types/Items/Glassmaker/CorpusBadgeMarsHiddenEvidence", + "/Lotus/Types/Items/Glassmaker/WeaveEvidencePartTwo", + "Glassmaker3", + "/Lotus/Types/Items/Glassmaker/GrineerTechCEvidence", + "/Lotus/Types/Items/Glassmaker/GrineerWeaponBEvidence", + "/Lotus/Types/Items/Glassmaker/GrineerPlushAEvidence", + "/Lotus/Types/Items/Glassmaker/GrineerMessageBEvidence", + "/Lotus/Types/Items/Glassmaker/GrineerDatapadBEvidence", + "/Lotus/Types/Items/Glassmaker/WeaveEvidencePartThree", + "TL20Start", + "TL20End", + "EudicoHeists", + "Glassmaker4", + "/Lotus/Types/Items/Glassmaker/OrokinManifestoBEvidence", + "/Lotus/Types/Items/Glassmaker/OrokinDatapadBEvidence", + "/Lotus/Types/Items/Glassmaker/OrokinNihilPlanCEvidence", + "/Lotus/Types/Items/Glassmaker/OrokinAppraisalBEvidence", + "/Lotus/Types/Items/Glassmaker/OrokinDossierAEvidence", + "/Lotus/Types/Items/Glassmaker/WeaveEvidencePartFour", + "/Lotus/Language/SolarisVenus/LegsShopNameLoved", + "/Lotus/Language/SolarisVenus/EudicoLoved", + "/Lotus/Language/SolarisVenus/ProspectorNameLoved", + "/Lotus/Language/SolarisVenus/StockbrokerNameLoved", + "/Lotus/Language/SolarisVenus/WeaponsmithShopNameLoved", + "/Lotus/Language/SolarisVenus/FishmongerNameLoved", + "DeimosHub", + "EntratiSyndicate", + "/Lotus/Language/InfestedMicroplanet/HivemindTokenVendorName", + "/Lotus/Language/InfestedMicroplanet/HivemindProspector", + "/Lotus/Language/InfestedMicroplanet/HivemindGunsmithName", + "/Lotus/Language/InfestedMicroplanet/HivemindPetVendor", + "ModularCrafting45", + "ModularCrafting55", + "NecraloidSyndicate", + "/Lotus/Language/InfestedMicroplanet/HivemindMechsName", + "/Lotus/Language/Npcs/EntratiMother", + "/Lotus/Language/InfestedMicroplanet/HivemindFishmonger", + "/Lotus/Language/SolarisVenus/SolarisUnitedAgentLoved", + "Glassmaker5", + "/Lotus/Types/Items/Glassmaker/InfestedOroShardsAEvidence", + "/Lotus/Types/Items/Glassmaker/InfestedOroWeaponCEvidence", + "/Lotus/Types/Items/Glassmaker/InfestedOroProbeBEvidence", + "/Lotus/Types/Items/Glassmaker/InfestedOroShackleBEvidence", + "/Lotus/Types/Items/Glassmaker/InfestedOroTabletBEvidence", + "/Lotus/Types/Items/Glassmaker/WeaveEvidencePartFive", + "GlassmakerBossFight", + "/Lotus/Language/InfestedMicroplanet/HivemindGunsmithNameLoved", + "/Lotus/Language/InfestedMicroplanet/HivemindTokenVendorNameLoved", + "/Lotus/Language/Npcs/EntratiMotherLoved", + "/Lotus/Language/InfestedMicroplanet/HivemindPetVendorLoved", + "/Lotus/Language/InfestedMicroplanet/HivemindFishmongerLoved", + "/Lotus/Language/InfestedMicroplanet/HivemindProspectorLoved", + "/Lotus/Language/InfestedMicroplanet/HivemindMechsNameLoved", + "/Lotus/Language/InfestedMicroplanet/HivemindPetVendorName", + "/Lotus/Language/InfestedMicroplanet/HivemindPetVendorNameLoved", + "/Lotus/Language/InfestedMicroplanet/HivemindFishmongerName", + "/Lotus/Language/InfestedMicroplanet/HivemindFishmongerNameLoved", + "/Lotus/Language/InfestedMicroplanet/HivemindProspectorName", + "/Lotus/Language/InfestedMicroplanet/HivemindProspectorNameLoved", + "DebtTokenVendorCrewMembers_FirstVisit", + "ZarimanSyndicate", + "/Lotus/Language/Npcs/Kahl", + "RankZeroConversationOne", + "RankZeroConversationTwo", + "EntratiLabSyndicate", + "/Lotus/Language/EntratiLab/EntratiGeneral/HumanLoid", + "/Lotus/Language/EntratiLab/EntratiGeneral/Fibonacci", + "/Lotus/Language/EntratiLab/EntratiGeneral/BirdThree", + "/Lotus/Language/Zariman/Quinn", + "/Lotus/Language/EntratiLab/EntratiGeneral/TagferFirstRank1", + "VoidVaultIntro", + "PurchasePlatformLockedNotificationSeen", + "/Lotus/Language/Zariman/Yonta", + "ZarimanSyndicateFirstOpen", + "HivemindTokenVendorBarter_FirstVisit", + "OtakLastWishManifest_FirstVisit", + "/Lotus/Language/Zariman/Cavalero", + "/Lotus/Language/Duviri/Acrithis", + "/Lotus/Language/Npcs/PrimeVaultTrader", + "/Lotus/Language/Zariman/Hombask", + "EntratiLabDisruptionManifest_FirstVisit", + "/Lotus/Language/EntratiLab/EntratiGeneral/BirdThreeLoved", + "/Lotus/Language/EntratiLab/EntratiGeneral/TagferFirstRank5", + "DanteLeverian", + "/Lotus/Language/EntratiLab/EntratiGeneral/HumanLoidLoved", + "ConquestSetupIntro", + "EntratiLabConquestHardModeUnlocked", + "/Lotus/Language/Npcs/KonzuPostNewWar" +] diff --git a/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json b/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json new file mode 100644 index 00000000..df3dc048 --- /dev/null +++ b/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json @@ -0,0 +1,38 @@ +{ + "VendorInfo": { + "_id": { "$oid": "62695b0467e5d379750f9f75" }, + "TypeName": "/Lotus/Types/Game/VendorManifests/Zariman/ArchimedeanVendorManifest", + "ItemManifest": [ + { + "StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/VoidPlumeAOrnament", + "ItemPrices": [{ "ItemCount": 1, "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidAngelItem", "ProductCategory": "MiscItems" }], + "Bin": "BIN_0", + "QuantityMultiplier": 1, + "Expiry": { "$date": { "$numberLong": "9999999000000" } }, + "AllowMultipurchase": true, + "Id": { "$oid": "63ed01ef4c37f93d0b797826" } + }, + { + "StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/VoidPlumeBOrnament", + "ItemPrices": [{ "ItemCount": 1, "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidAngelItem", "ProductCategory": "MiscItems" }], + "Bin": "BIN_0", + "QuantityMultiplier": 1, + "Expiry": { "$date": { "$numberLong": "9999999000000" } }, + "AllowMultipurchase": true, + "Id": { "$oid": "63ed01ef4c37f93d0b797827" } + }, + { + "StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva", + "ItemPrices": [{ "ItemCount": 5, "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidAngelItem", "ProductCategory": "MiscItems" }], + "Bin": "BIN_0", + "QuantityMultiplier": 35000, + "Expiry": { "$date": { "$numberLong": "9999999000000" } }, + "PurchaseQuantityLimit": 1, + "AllowMultipurchase": false, + "Id": { "$oid": "66664112af1177b5070ab882" } + } + ], + "PropertyTextHash": "DB7BF03C3FE6D0036A4DC30066A9A17E", + "Expiry": { "$date": { "$numberLong": "9999999000000" } } + } +} diff --git a/static/fixed_responses/getVendorInfo.json b/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json similarity index 100% rename from static/fixed_responses/getVendorInfo.json rename to static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json diff --git a/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json b/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json new file mode 100644 index 00000000..ec1c5c22 --- /dev/null +++ b/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json @@ -0,0 +1,75 @@ +{ + "VendorInfo": { + "_id": { "$oid": "62a20ba667e5d3797540d831" }, + "TypeName": "/Lotus/Types/Game/VendorManifests/Zariman/ZarimanCommisionsManifestArchimedean", + "ItemManifest": [ + { + "StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskE", + "ItemPrices": [ + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB", "ItemCount": 4, "ProductCategory": "MiscItems" }, + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemA", "ItemCount": 6, "ProductCategory": "MiscItems" } + ], + "Bin": "BIN_4", + "QuantityMultiplier": 1, + "Expiry": { "$date": { "$numberLong": "9999999000000" } }, + "PurchaseQuantityLimit": 1, + "AllowMultipurchase": false, + "Id": { "$oid": "6678b612aa3d8ee5c2597299" } + }, + { + "StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskD", + "ItemPrices": [ + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemA", "ItemCount": 5, "ProductCategory": "MiscItems" }, + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB", "ItemCount": 3, "ProductCategory": "MiscItems" } + ], + "Bin": "BIN_3", + "QuantityMultiplier": 1, + "Expiry": { "$date": { "$numberLong": "9999999000000" } }, + "PurchaseQuantityLimit": 1, + "AllowMultipurchase": false, + "Id": { "$oid": "6678b612aa3d8ee5c259729a" } + }, + { + "StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskC", + "ItemPrices": [ + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidWraithItem", "ItemCount": 15, "ProductCategory": "MiscItems" }, + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanDogTagUncommon", "ItemCount": 1, "ProductCategory": "MiscItems" } + ], + "Bin": "BIN_2", + "QuantityMultiplier": 1, + "Expiry": { "$date": { "$numberLong": "9999999000000" } }, + "PurchaseQuantityLimit": 1, + "AllowMultipurchase": false, + "Id": { "$oid": "6678b612aa3d8ee5c259729b" } + }, + { + "StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskB", + "ItemPrices": [ + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidWraithItem", "ItemCount": 4, "ProductCategory": "MiscItems" }, + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB", "ItemCount": 1, "ProductCategory": "MiscItems" } + ], + "Bin": "BIN_1", + "QuantityMultiplier": 1, + "Expiry": { "$date": { "$numberLong": "9999999000000" } }, + "PurchaseQuantityLimit": 1, + "AllowMultipurchase": false, + "Id": { "$oid": "6678b612aa3d8ee5c259729c" } + }, + { + "StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskA", + "ItemPrices": [ + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB", "ItemCount": 1, "ProductCategory": "MiscItems" }, + { "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemA", "ItemCount": 2, "ProductCategory": "MiscItems" } + ], + "Bin": "BIN_0", + "QuantityMultiplier": 1, + "Expiry": { "$date": { "$numberLong": "9999999000000" } }, + "PurchaseQuantityLimit": 1, + "AllowMultipurchase": false, + "Id": { "$oid": "6678b612aa3d8ee5c259729d" } + } + ], + "PropertyTextHash": "F43F0ED811985EEF856970A8342EF322", + "Expiry": { "$date": { "$numberLong": "9999999000000" } } + } +} diff --git a/static/fixed_responses/worldState.json b/static/fixed_responses/worldState.json index aa30c06b..caeae2ae 100644 --- a/static/fixed_responses/worldState.json +++ b/static/fixed_responses/worldState.json @@ -1,21 +1,21 @@ { "Events": [ { + "Msg": "Join the OpenWF Discord!", "Messages": [ - { "LanguageCode": "en", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "fr", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "it", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "de", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "es", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "pt", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "ru", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "pl", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "uk", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "tr", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "ja", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "zh", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "ko", "Message": "Join the OpenWF Discord!" }, - { "LanguageCode": "tc", "Message": "Join the OpenWF Discord!" } + { "LanguageCode": "fr", "Message": "Rejoignez le Discord OpenWF!" }, + { "LanguageCode": "it", "Message": "Unisciti al Discord di OpenWF!" }, + { "LanguageCode": "de", "Message": "Tritt dem OpenWF Discord bei!" }, + { "LanguageCode": "es", "Message": "Únete al Discord de OpenWF!" }, + { "LanguageCode": "pt", "Message": "Junte-se ao Discord do OpenWF!" }, + { "LanguageCode": "ru", "Message": "Присоединяйтесь к OpenWF Discord!" }, + { "LanguageCode": "pl", "Message": "Dołącz do Discord OpenWF!" }, + { "LanguageCode": "uk", "Message": "Приєднуйтесь до OpenWF Discord!" }, + { "LanguageCode": "tr", "Message": "OpenWF Discord'a katıl!" }, + { "LanguageCode": "ja", "Message": "OpenWFのDiscordに参加しよう!" }, + { "LanguageCode": "zh", "Message": "加入OpenWF Discord!" }, + { "LanguageCode": "ko", "Message": "OpenWF Discord에 가입하세요!" }, + { "LanguageCode": "tc", "Message": "加入OpenWF Discord!" } ], "Prop": "https://discord.gg/PNNZ3asUuY", "Icon": "/Lotus/Interface/Icons/DiscordIconNoBacker.png" diff --git a/static/webui/index.html b/static/webui/index.html index 5b49578e..533b2f20 100644 --- a/static/webui/index.html +++ b/static/webui/index.html @@ -114,7 +114,7 @@

You can use these unlimited slots to apply a wide range of upgrades.

- + x @@ -188,6 +188,10 @@
+
+ + +
diff --git a/static/webui/script.js b/static/webui/script.js index dd153061..db5b831c 100644 --- a/static/webui/script.js +++ b/static/webui/script.js @@ -731,6 +731,7 @@ const uiConfigs = [ "autoCreateAccount", "skipStoryModeChoice", "skipTutorial", + "skipAllDialogue", "unlockAllScans", "unlockAllMissions", "unlockAllQuests", diff --git a/tsconfig.json b/tsconfig.json index a75f01a3..c2603f08 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,4 @@ { - "extends": "@tsconfig/node20/tsconfig.json", "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ @@ -12,8 +11,8 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - //"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": ["es2023"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "lib": ["es2023"] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ @@ -26,7 +25,7 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - //"module": "commonjs" /* Specify what module code is generated. */, + "module": "node16" /* Specify what module code is generated. */, // "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */