Compare commits
1 Commits
main
...
no-vallis-
Author | SHA1 | Date | |
---|---|---|---|
54459cf9d0 |
45
Dockerfile
45
Dockerfile
@ -5,40 +5,17 @@ ENV APP_MY_ADDRESS=localhost
|
||||
ENV APP_HTTP_PORT=80
|
||||
ENV APP_HTTPS_PORT=443
|
||||
ENV APP_AUTO_CREATE_ACCOUNT=true
|
||||
ENV APP_SKIP_TUTORIAL=false
|
||||
ENV APP_SKIP_ALL_DIALOGUE=false
|
||||
ENV APP_UNLOCK_ALL_SCANS=false
|
||||
ENV APP_UNLOCK_ALL_MISSIONS=false
|
||||
ENV APP_INFINITE_CREDITS=false
|
||||
ENV APP_INFINITE_PLATINUM=false
|
||||
ENV APP_INFINITE_ENDO=false
|
||||
ENV APP_INFINITE_REGAL_AYA=false
|
||||
ENV APP_INFINITE_HELMINTH_MATERIALS=false
|
||||
ENV APP_DONT_SUBTRACT_CONSUMABLES=false
|
||||
ENV APP_UNLOCK_ALL_SHIP_FEATURES=false
|
||||
ENV APP_UNLOCK_ALL_SHIP_DECORATIONS=false
|
||||
ENV APP_UNLOCK_ALL_FLAVOUR_ITEMS=false
|
||||
ENV APP_UNLOCK_ALL_SKINS=false
|
||||
ENV APP_UNLOCK_ALL_CAPTURA_SCENES=false
|
||||
ENV APP_UNIVERSAL_POLARITY_EVERYWHERE=false
|
||||
ENV APP_UNLOCK_DOUBLE_CAPACITY_POTATOES_EVERYWHERE=false
|
||||
ENV APP_UNLOCK_EXILUS_EVERYWHERE=false
|
||||
ENV APP_UNLOCK_ARCANES_EVERYWHERE=false
|
||||
ENV APP_NO_DAILY_FOCUS_LIMIT=false
|
||||
ENV APP_NO_ARGON_CRYSTAL_DECAY=false
|
||||
ENV APP_NO_MASTERY_RANK_UP_COOLDOWN=false
|
||||
ENV APP_NO_VENDOR_PURCHASE_LIMITS=true
|
||||
ENV APP_NO_DEATH_MARKS=false
|
||||
ENV APP_NO_KIM_COOLDOWNS=false
|
||||
ENV APP_INSTANT_RESOURCE_EXTRACTOR_DRONES=false
|
||||
ENV APP_NO_RESOURCE_EXTRACTOR_DRONES_DAMAGE=false
|
||||
ENV APP_SKIP_CLAN_KEY_CRAFTING=false
|
||||
ENV APP_NO_DOJO_ROOM_BUILD_STAGE=false
|
||||
ENV APP_NO_DECO_BUILD_STAGE=false
|
||||
ENV APP_FAST_DOJO_ROOM_DESTRUCTION=false
|
||||
ENV APP_NO_DOJO_RESEARCH_COSTS=false
|
||||
ENV APP_NO_DOJO_RESEARCH_TIME=false
|
||||
ENV APP_FAST_CLAN_ASCENSION=false
|
||||
ENV APP_SKIP_STORY_MODE_CHOICE=true
|
||||
ENV APP_SKIP_TUTORIAL=true
|
||||
ENV APP_SKIP_ALL_DIALOGUE=true
|
||||
ENV APP_UNLOCK_ALL_SCANS=true
|
||||
ENV APP_UNLOCK_ALL_MISSIONS=true
|
||||
ENV APP_INFINITE_RESOURCES=true
|
||||
ENV APP_UNLOCK_ALL_SHIP_FEATURES=true
|
||||
ENV APP_UNLOCK_ALL_SHIP_DECORATIONS=true
|
||||
ENV APP_UNLOCK_ALL_FLAVOUR_ITEMS=true
|
||||
ENV APP_UNLOCK_ALL_SKINS=true
|
||||
ENV APP_UNIVERSAL_POLARITY_EVERYWHERE=true
|
||||
ENV APP_SPOOF_MASTERY_RANK=-1
|
||||
|
||||
RUN apk add --no-cache bash sed wget jq
|
||||
|
@ -1,6 +1,7 @@
|
||||
@echo off
|
||||
|
||||
echo Updating SpaceNinjaServer...
|
||||
git config remote.origin.url https://openwf.io/SpaceNinjaServer.git
|
||||
git fetch --prune
|
||||
git stash
|
||||
git reset --hard origin/main
|
||||
|
@ -19,7 +19,6 @@
|
||||
"infiniteEndo": false,
|
||||
"infiniteRegalAya": false,
|
||||
"infiniteHelminthMaterials": false,
|
||||
"dontSubtractConsumables": false,
|
||||
"unlockAllShipFeatures": false,
|
||||
"unlockAllShipDecorations": false,
|
||||
"unlockAllFlavourItems": false,
|
||||
@ -38,7 +37,6 @@
|
||||
"noKimCooldowns": false,
|
||||
"instantResourceExtractorDrones": false,
|
||||
"noResourceExtractorDronesDamage": false,
|
||||
"skipClanKeyCrafting": false,
|
||||
"noDojoRoomBuildStage": false,
|
||||
"noDecoBuildStage": false,
|
||||
"fastDojoRoomDestruction": false,
|
||||
|
@ -12,40 +12,19 @@ services:
|
||||
# APP_HTTP_PORT: 80
|
||||
# APP_HTTPS_PORT: 443
|
||||
# APP_AUTO_CREATE_ACCOUNT: true
|
||||
# APP_SKIP_TUTORIAL: false
|
||||
# APP_SKIP_ALL_DIALOGUE: false
|
||||
# APP_UNLOCK_ALL_SCANS: false
|
||||
# APP_UNLOCK_ALL_MISSIONS: false
|
||||
# APP_INFINITE_CREDITS: false
|
||||
# APP_INFINITE_PLATINUM: false
|
||||
# APP_INFINITE_ENDO: false
|
||||
# APP_INFINITE_REGAL_AYA: false
|
||||
# APP_INFINITE_HELMINTH_MATERIALS: false
|
||||
# APP_DONT_SUBTRACT_CONSUMABLES: false
|
||||
# APP_UNLOCK_ALL_SHIP_FEATURES: false
|
||||
# APP_UNLOCK_ALL_SHIP_DECORATIONS: false
|
||||
# APP_UNLOCK_ALL_FLAVOUR_ITEMS: false
|
||||
# APP_UNLOCK_ALL_SKINS: false
|
||||
# APP_UNLOCK_ALL_CAPTURA_SCENES: false
|
||||
# APP_UNIVERSAL_POLARITY_EVERYWHERE: false
|
||||
# APP_UNLOCK_DOUBLE_CAPACITY_POTATOES_EVERYWHERE: false
|
||||
# APP_UNLOCK_EXILUS_EVERYWHERE: false
|
||||
# APP_UNLOCK_ARCANES_EVERYWHERE: false
|
||||
# APP_NO_DAILY_FOCUS_LIMIT: false
|
||||
# APP_NO_ARGON_CRYSTAL_DECAY: false
|
||||
# APP_NO_MASTERY_RANK_UP_COOLDOWN: false
|
||||
# APP_NO_VENDOR_PURCHASE_LIMITS: true
|
||||
# APP_NO_DEATH_MARKS: false
|
||||
# APP_NO_KIM_COOLDOWNS: false
|
||||
# APP_INSTANT_RESOURCE_EXTRACTOR_DRONES: false
|
||||
# APP_NO_RESOURCE_EXTRACTOR_DRONES_DAMAGE: false
|
||||
# APP_SKIP_CLAN_KEY_CRAFTING: false
|
||||
# APP_NO_DOJO_ROOM_BUILD_STAGE: false
|
||||
# APP_NO_DECO_BUILD_STAGE: false
|
||||
# APP_FAST_DOJO_ROOM_DESTRUCTION: false
|
||||
# APP_NO_DOJO_RESEARCH_COSTS: false
|
||||
# APP_NO_DOJO_RESEARCH_TIME: false
|
||||
# APP_FAST_CLAN_ASCENSION: false
|
||||
# APP_SKIP_STORY_MODE_CHOICE: true
|
||||
# APP_SKIP_TUTORIAL: true
|
||||
# APP_SKIP_ALL_DIALOGUE: true
|
||||
# APP_UNLOCK_ALL_SCANS: true
|
||||
# APP_UNLOCK_ALL_MISSIONS: true
|
||||
# APP_UNLOCK_ALL_QUESTS: true
|
||||
# APP_COMPLETE_ALL_QUESTS: true
|
||||
# APP_INFINITE_RESOURCES: true
|
||||
# APP_UNLOCK_ALL_SHIP_FEATURES: true
|
||||
# APP_UNLOCK_ALL_SHIP_DECORATIONS: true
|
||||
# APP_UNLOCK_ALL_FLAVOUR_ITEMS: true
|
||||
# APP_UNLOCK_ALL_SKINS: true
|
||||
# APP_UNIVERSAL_POLARITY_EVERYWHERE: true
|
||||
# APP_SPOOF_MASTERY_RANK: -1
|
||||
volumes:
|
||||
- ./docker-data/static:/app/static/data
|
||||
|
@ -19,6 +19,5 @@ do
|
||||
mv config.tmp config.json
|
||||
done
|
||||
|
||||
npm i --omit=dev
|
||||
npm run build
|
||||
exec npm run start
|
||||
npm install
|
||||
exec npm run dev
|
||||
|
498
package-lock.json
generated
498
package-lock.json
generated
@ -18,7 +18,7 @@
|
||||
"morgan": "^1.10.0",
|
||||
"ncp": "^2.0.0",
|
||||
"typescript": "^5.5",
|
||||
"warframe-public-export-plus": "^0.5.60",
|
||||
"warframe-public-export-plus": "^0.5.56",
|
||||
"warframe-riven-info": "^0.1.2",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
@ -72,9 +72,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
||||
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.0.tgz",
|
||||
"integrity": "sha512-RoV8Xs9eNwiDvhv7M+xcL4PWyRyIXRY/FLp3buU4h1EYfdF7unWUy3dOjPqb3C7rMUewIcqwW850PgS8h1o1yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -249,9 +249,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mongodb-js/saslprep": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz",
|
||||
"integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.0.tgz",
|
||||
"integrity": "sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
@ -296,22 +296,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz",
|
||||
"integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==",
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.0.tgz",
|
||||
"integrity": "sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/@rxliuli/tsgo": {
|
||||
"version": "2025.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@rxliuli/tsgo/-/tsgo-2025.5.8.tgz",
|
||||
"integrity": "sha512-P3/qxcUgiWz6nSJslJ5mMeAEqacK8LQSoOhdvHxI1/d0Xqxt2Qp6/nmhWuOlyqnCyAaIoXgoiUshiXWBGr2jaw==",
|
||||
"version": "2025.3.31",
|
||||
"resolved": "https://registry.npmjs.org/@rxliuli/tsgo/-/tsgo-2025.3.31.tgz",
|
||||
"integrity": "sha512-jEistRy/+Mu79rDv/Q8xn2yIM56WF3rfQOkwrbtivumij5HBVTfY4W3EYNL3N7rop7yg9Trew3joDohDoxQ2Ow==",
|
||||
"cpu": [
|
||||
"x64",
|
||||
"ia32",
|
||||
@ -382,13 +382,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
|
||||
"integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz",
|
||||
"integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
@ -426,12 +427,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.15.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.16.tgz",
|
||||
"integrity": "sha512-3pr+KjwpVujqWqOKT8mNR+rd09FqhBLwg+5L/4t0cNYBzm/yEiYGCxWttjaPBsLtAo+WFNoXzGJfolM1JuRXoA==",
|
||||
"version": "22.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
@ -503,21 +504,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.0.tgz",
|
||||
"integrity": "sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ==",
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.28.0.tgz",
|
||||
"integrity": "sha512-lvFK3TCGAHsItNdWZ/1FkvpzCxTHUVuFrdnOGLMa0GGCFIbCgQWVk3CzCGdA7kM3qGVc+dfW9tr0Z/sHnGDFyg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.0",
|
||||
"@typescript-eslint/type-utils": "8.32.0",
|
||||
"@typescript-eslint/utils": "8.32.0",
|
||||
"@typescript-eslint/visitor-keys": "8.32.0",
|
||||
"@typescript-eslint/scope-manager": "8.28.0",
|
||||
"@typescript-eslint/type-utils": "8.28.0",
|
||||
"@typescript-eslint/utils": "8.28.0",
|
||||
"@typescript-eslint/visitor-keys": "8.28.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.3.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -533,16 +534,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.0.tgz",
|
||||
"integrity": "sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==",
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.28.0.tgz",
|
||||
"integrity": "sha512-LPcw1yHD3ToaDEoljFEfQ9j2xShY367h7FZ1sq5NJT9I3yj4LHer1Xd1yRSOdYy9BpsrxU7R+eoDokChYM53lQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.32.0",
|
||||
"@typescript-eslint/types": "8.32.0",
|
||||
"@typescript-eslint/typescript-estree": "8.32.0",
|
||||
"@typescript-eslint/visitor-keys": "8.32.0",
|
||||
"@typescript-eslint/scope-manager": "8.28.0",
|
||||
"@typescript-eslint/types": "8.28.0",
|
||||
"@typescript-eslint/typescript-estree": "8.28.0",
|
||||
"@typescript-eslint/visitor-keys": "8.28.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@ -558,14 +559,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.0.tgz",
|
||||
"integrity": "sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==",
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.28.0.tgz",
|
||||
"integrity": "sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.32.0",
|
||||
"@typescript-eslint/visitor-keys": "8.32.0"
|
||||
"@typescript-eslint/types": "8.28.0",
|
||||
"@typescript-eslint/visitor-keys": "8.28.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -576,16 +577,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.0.tgz",
|
||||
"integrity": "sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==",
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.28.0.tgz",
|
||||
"integrity": "sha512-oRoXu2v0Rsy/VoOGhtWrOKDiIehvI+YNrDk5Oqj40Mwm0Yt01FC/Q7nFqg088d3yAsR1ZcZFVfPCTTFCe/KPwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.32.0",
|
||||
"@typescript-eslint/utils": "8.32.0",
|
||||
"@typescript-eslint/typescript-estree": "8.28.0",
|
||||
"@typescript-eslint/utils": "8.28.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -600,9 +601,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.0.tgz",
|
||||
"integrity": "sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==",
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.28.0.tgz",
|
||||
"integrity": "sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -614,20 +615,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.0.tgz",
|
||||
"integrity": "sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==",
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.28.0.tgz",
|
||||
"integrity": "sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.32.0",
|
||||
"@typescript-eslint/visitor-keys": "8.32.0",
|
||||
"@typescript-eslint/types": "8.28.0",
|
||||
"@typescript-eslint/visitor-keys": "8.28.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "^9.0.4",
|
||||
"semver": "^7.6.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
"ts-api-utils": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -641,16 +642,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.0.tgz",
|
||||
"integrity": "sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==",
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.28.0.tgz",
|
||||
"integrity": "sha512-OELa9hbTYciYITqgurT1u/SzpQVtDLmQMFzy/N8pQE+tefOyCWT79jHsav294aTqV1q1u+VzqDGbuujvRYaeSQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.32.0",
|
||||
"@typescript-eslint/types": "8.32.0",
|
||||
"@typescript-eslint/typescript-estree": "8.32.0"
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "8.28.0",
|
||||
"@typescript-eslint/types": "8.28.0",
|
||||
"@typescript-eslint/typescript-estree": "8.28.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@ -665,13 +666,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.0.tgz",
|
||||
"integrity": "sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==",
|
||||
"version": "8.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.28.0.tgz",
|
||||
"integrity": "sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.32.0",
|
||||
"@typescript-eslint/types": "8.28.0",
|
||||
"eslint-visitor-keys": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -867,16 +868,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
||||
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.1.0.tgz",
|
||||
"integrity": "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "^3.1.2",
|
||||
"content-type": "^1.0.5",
|
||||
"debug": "^4.4.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"iconv-lite": "^0.5.2",
|
||||
"on-finished": "^2.4.1",
|
||||
"qs": "^6.14.0",
|
||||
"raw-body": "^3.0.0",
|
||||
@ -886,6 +887,21 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
@ -1121,9 +1137,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@ -1205,6 +1221,16 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
@ -1380,14 +1406,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.0.tgz",
|
||||
"integrity": "sha512-BvQOvUhkVQM1i63iMETK9Hjud9QhqBnbtT1Zc642p9ynzBuCe5pybkOnvqZIBypXmMlsGcnU4HZ8sCTPfpAexA==",
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.5.tgz",
|
||||
"integrity": "sha512-IKKP8R87pJyMl7WWamLgPkloB16dagPIdd2FjBDbyRYPKo93wS/NbCOPh6gH+ieNLC+XZrhJt/kWj0PS/DFdmg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prettier-linter-helpers": "^1.0.0",
|
||||
"synckit": "^0.11.0"
|
||||
"synckit": "^0.10.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
@ -1538,47 +1564,71 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz",
|
||||
"integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.0",
|
||||
"body-parser": "^2.0.1",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "^1.0.5",
|
||||
"cookie": "^0.7.1",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "^1.2.1",
|
||||
"debug": "^4.4.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"finalhandler": "^2.1.0",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"debug": "4.3.6",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "^2.0.0",
|
||||
"fresh": "2.0.0",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "^2.0.0",
|
||||
"methods": "~1.1.2",
|
||||
"mime-types": "^3.0.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"once": "^1.4.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"qs": "^6.14.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"router": "^2.2.0",
|
||||
"on-finished": "2.4.1",
|
||||
"once": "1.4.0",
|
||||
"parseurl": "~1.3.3",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"router": "^2.0.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "^1.1.0",
|
||||
"serve-static": "^2.2.0",
|
||||
"statuses": "^2.0.1",
|
||||
"type-is": "^2.0.1",
|
||||
"vary": "^1.1.2"
|
||||
"serve-static": "^2.1.0",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "^2.0.0",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@ -1981,12 +2031,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
|
||||
"integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -2194,9 +2244,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-with-bigint": {
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.4.4.tgz",
|
||||
"integrity": "sha512-AhpYAAaZsPjU7smaBomDt1SOQshi9rEm6BlTbfVwsG1vNmeHKtEedJi62sHZzJTyKNtwzmNnrsd55kjwJ7054A==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.2.2.tgz",
|
||||
"integrity": "sha512-zbaZ+MZ2PEcAD0yINpxvlLMKzoC1GPqy5p8/ZgzRJRoB+NCczGrTX9x2ashSvkfYTitQKbV5aYQCJCiHxrzF2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json5": {
|
||||
@ -2344,6 +2394,15 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
@ -2359,21 +2418,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"version": "1.53.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz",
|
||||
"integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
||||
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz",
|
||||
"integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.54.0"
|
||||
"mime-db": "^1.53.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@ -2428,9 +2487,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.16.0.tgz",
|
||||
"integrity": "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==",
|
||||
"version": "6.14.2",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.14.2.tgz",
|
||||
"integrity": "sha512-kMEHNo0F3P6QKDq17zcDuPeaywK/YaJVCEQRzPF3TOM/Bl9MFg64YE5Tu7ifj37qZJMhwU1tl2Ioivws5gRG5Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@mongodb-js/saslprep": "^1.1.9",
|
||||
@ -2484,14 +2543,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.14.1.tgz",
|
||||
"integrity": "sha512-ijd12vjqUBr5Btqqflu0c/o8Oed5JpdaE0AKO9TjGxCgywYwnzt6ynR1ySjhgxGxrYVeXC0t1P11f1zlRiE93Q==",
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.12.1.tgz",
|
||||
"integrity": "sha512-UW22y8QFVYmrb36hm8cGncfn4ARc/XsYWQwRTaj0gxtQk1rDuhzDO1eBantS+hTTatfAIS96LlRCJrcNHvW5+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bson": "^6.10.3",
|
||||
"kareem": "2.6.3",
|
||||
"mongodb": "~6.16.0",
|
||||
"mongodb": "~6.14.0",
|
||||
"mpath": "0.9.0",
|
||||
"mquery": "5.0.0",
|
||||
"ms": "2.1.3",
|
||||
@ -2863,12 +2922,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
@ -2922,18 +2981,16 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
@ -3009,13 +3066,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz",
|
||||
"integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"is-promise": "^4.0.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"path-to-regexp": "^8.0.0"
|
||||
@ -3097,18 +3152,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
||||
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz",
|
||||
"integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.5",
|
||||
"destroy": "^1.2.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"fresh": "^2.0.0",
|
||||
"fresh": "^0.5.2",
|
||||
"http-errors": "^2.0.0",
|
||||
"mime-types": "^3.0.1",
|
||||
"mime-types": "^2.1.35",
|
||||
"ms": "^2.1.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"range-parser": "^1.2.1",
|
||||
@ -3118,16 +3174,46 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
||||
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.1.0.tgz",
|
||||
"integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"parseurl": "^1.3.3",
|
||||
"send": "^1.2.0"
|
||||
"send": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
@ -3297,15 +3383,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
@ -3369,20 +3446,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz",
|
||||
"integrity": "sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ==",
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.10.3.tgz",
|
||||
"integrity": "sha512-R1urvuyiTaWfeCggqEvpDJwAlDVdsT9NM+IP//Tk2x7qHCkSvBk/fwFgw/TLAHzZlrAnnazMcRw0ZD8HlYFTEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.2.3",
|
||||
"@pkgr/core": "^0.2.0",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/synckit"
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/text-hex": {
|
||||
@ -3421,9 +3498,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
|
||||
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
|
||||
"integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"punycode": "^2.3.1"
|
||||
@ -3629,9 +3706,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.0.tgz",
|
||||
"integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
@ -3656,9 +3733,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
@ -3686,6 +3763,15 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
@ -3703,9 +3789,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/warframe-public-export-plus": {
|
||||
"version": "0.5.60",
|
||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.60.tgz",
|
||||
"integrity": "sha512-vMfytUc4xRi+b7RTSq+TJEl91vwEegpQKxLtXwRPfs9ZHhntxc4rmDYSNWJTvgf/aWXsFUxQlqL/GV5OLPGM7g=="
|
||||
"version": "0.5.56",
|
||||
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.56.tgz",
|
||||
"integrity": "sha512-px+J7tUm6fkSzwKkvL73ySQReDq9oM1UrHSLM3vbYGBvELM892iBgPYG45okIhScCSdwmmXTiWZTf4x/I4qiNQ=="
|
||||
},
|
||||
"node_modules/warframe-riven-info": {
|
||||
"version": "0.1.2",
|
||||
@ -3722,12 +3808,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
|
||||
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz",
|
||||
"integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "^5.1.0",
|
||||
"tr46": "^5.0.0",
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -3804,6 +3890,52 @@
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/winston-transport/node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/winston-transport/node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/winston/node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/winston/node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
|
@ -25,7 +25,7 @@
|
||||
"morgan": "^1.10.0",
|
||||
"ncp": "^2.0.0",
|
||||
"typescript": "^5.5",
|
||||
"warframe-public-export-plus": "^0.5.60",
|
||||
"warframe-public-export-plus": "^0.5.56",
|
||||
"warframe-riven-info": "^0.1.2",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
|
@ -21,13 +21,6 @@ app.use((req, _res, next) => {
|
||||
if (req.headers["content-encoding"] == "ezip" || req.headers["content-encoding"] == "e") {
|
||||
req.headers["content-encoding"] = undefined;
|
||||
}
|
||||
|
||||
// U18 uses application/x-www-form-urlencoded even tho the data is JSON which Express doesn't like.
|
||||
// U17 sets no Content-Type at all, which Express also doesn't like.
|
||||
if (!req.headers["content-type"] || req.headers["content-type"] == "application/x-www-form-urlencoded") {
|
||||
req.headers["content-type"] = "application/octet-stream";
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
|
@ -2,18 +2,15 @@ const millisecondsPerSecond = 1000;
|
||||
const secondsPerMinute = 60;
|
||||
const minutesPerHour = 60;
|
||||
const hoursPerDay = 24;
|
||||
const daysPerWeek = 7;
|
||||
|
||||
const unixSecond = millisecondsPerSecond;
|
||||
const unixMinute = secondsPerMinute * millisecondsPerSecond;
|
||||
const unixHour = unixMinute * minutesPerHour;
|
||||
const unixDay = hoursPerDay * unixHour;
|
||||
const unixWeek = daysPerWeek * unixDay;
|
||||
|
||||
export const unixTimesInMs = {
|
||||
second: unixSecond,
|
||||
minute: unixMinute,
|
||||
hour: unixHour,
|
||||
day: unixDay,
|
||||
week: unixWeek
|
||||
day: unixDay
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ export const activateRandomModController: RequestHandler = async (req, res) => {
|
||||
ItemCount: -1
|
||||
}
|
||||
]);
|
||||
const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType])!;
|
||||
const rivenType = getRandomElement(rivenRawToRealWeighted[request.ItemType]);
|
||||
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
|
||||
const upgradeIndex =
|
||||
inventory.Upgrades.push({
|
||||
|
@ -1,60 +0,0 @@
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Friendship } from "@/src/models/friendModel";
|
||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "@/src/services/friendService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IFriendInfo } from "@/src/types/friendTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const addFriendController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const payload = getJSONfromString<IAddFriendRequest>(String(req.body));
|
||||
const promises: Promise<void>[] = [];
|
||||
const newFriends: IFriendInfo[] = [];
|
||||
if (payload.friend == "all") {
|
||||
const [internalFriendships, externalFriendships] = await Promise.all([
|
||||
Friendship.find({ owner: accountId }, "friend"),
|
||||
Friendship.find({ friend: accountId }, "owner")
|
||||
]);
|
||||
for (const externalFriendship of externalFriendships) {
|
||||
if (!internalFriendships.find(x => x.friend.equals(externalFriendship.owner))) {
|
||||
promises.push(
|
||||
Friendship.insertOne({
|
||||
owner: accountId,
|
||||
friend: externalFriendship.owner,
|
||||
Note: externalFriendship.Note // TOVERIFY: Should the note be copied when accepting a friend request?
|
||||
}) as unknown as Promise<void>
|
||||
);
|
||||
newFriends.push({
|
||||
_id: toOid(externalFriendship.owner)
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const externalFriendship = await Friendship.findOne({ owner: payload.friend, friend: accountId }, "Note");
|
||||
if (externalFriendship) {
|
||||
promises.push(
|
||||
Friendship.insertOne({
|
||||
owner: accountId,
|
||||
friend: payload.friend,
|
||||
Note: externalFriendship.Note
|
||||
}) as unknown as Promise<void>
|
||||
);
|
||||
newFriends.push({
|
||||
_id: { $oid: payload.friend }
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const newFriend of newFriends) {
|
||||
promises.push(addAccountDataToFriendInfo(newFriend));
|
||||
promises.push(addInventoryDataToFriendInfo(newFriend));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
res.json({
|
||||
Friends: newFriends
|
||||
});
|
||||
};
|
||||
|
||||
interface IAddFriendRequest {
|
||||
friend: string; // oid or "all" in which case all=1 is also a query parameter
|
||||
}
|
@ -2,7 +2,7 @@ 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/friendTypes";
|
||||
import { IFriendInfo } from "@/src/types/guildTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const addIgnoredUserController: RequestHandler = async (req, res) => {
|
||||
|
@ -1,52 +0,0 @@
|
||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Friendship } from "@/src/models/friendModel";
|
||||
import { Account } from "@/src/models/loginModel";
|
||||
import { addInventoryDataToFriendInfo, areFriendsOfFriends } from "@/src/services/friendService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IFriendInfo } from "@/src/types/friendTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const addPendingFriendController: RequestHandler = async (req, res) => {
|
||||
const payload = getJSONfromString<IAddPendingFriendRequest>(String(req.body));
|
||||
|
||||
const account = await Account.findOne({ DisplayName: payload.friend });
|
||||
if (!account) {
|
||||
res.status(400).end();
|
||||
return;
|
||||
}
|
||||
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(account._id.toString(), "Settings");
|
||||
if (
|
||||
inventory.Settings?.FriendInvRestriction == "GIFT_MODE_NONE" ||
|
||||
(inventory.Settings?.FriendInvRestriction == "GIFT_MODE_FRIENDS" &&
|
||||
!(await areFriendsOfFriends(account._id, accountId)))
|
||||
) {
|
||||
res.status(400).send("Friend Invite Restriction");
|
||||
return;
|
||||
}
|
||||
|
||||
await Friendship.insertOne({
|
||||
owner: accountId,
|
||||
friend: account._id,
|
||||
Note: payload.message
|
||||
});
|
||||
|
||||
const friendInfo: IFriendInfo = {
|
||||
_id: toOid(account._id),
|
||||
DisplayName: account.DisplayName,
|
||||
LastLogin: toMongoDate(account.LastLogin),
|
||||
Note: payload.message
|
||||
};
|
||||
await addInventoryDataToFriendInfo(friendInfo);
|
||||
res.json({
|
||||
Friend: friendInfo
|
||||
});
|
||||
};
|
||||
|
||||
interface IAddPendingFriendRequest {
|
||||
friend: string;
|
||||
message: string;
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { Account } from "@/src/models/loginModel";
|
||||
import { addInventoryDataToFriendInfo, areFriends } from "@/src/services/friendService";
|
||||
import { hasGuildPermission } from "@/src/services/guildService";
|
||||
import { fillInInventoryDataForGuildMember, hasGuildPermission } from "@/src/services/guildService";
|
||||
import { createMessage } from "@/src/services/inboxService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
@ -24,18 +22,15 @@ export const addToGuildController: RequestHandler = async (req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const senderAccount = await getAccountForRequest(req);
|
||||
const inventory = await getInventory(account._id.toString(), "Settings");
|
||||
if (
|
||||
inventory.Settings?.GuildInvRestriction == "GIFT_MODE_NONE" ||
|
||||
(inventory.Settings?.GuildInvRestriction == "GIFT_MODE_FRIENDS" &&
|
||||
!(await areFriends(account._id, senderAccount._id)))
|
||||
) {
|
||||
// TODO: Also consider GIFT_MODE_FRIENDS once friends are implemented
|
||||
if (inventory.Settings?.GuildInvRestriction == "GIFT_MODE_NONE") {
|
||||
res.status(400).json("Invite restricted");
|
||||
return;
|
||||
}
|
||||
|
||||
const guild = (await Guild.findById(payload.GuildId.$oid, "Name Ranks"))!;
|
||||
const senderAccount = await getAccountForRequest(req);
|
||||
if (!(await hasGuildPermission(guild, senderAccount._id.toString(), GuildPermission.Recruiter))) {
|
||||
res.status(400).json("Invalid permission");
|
||||
}
|
||||
@ -76,11 +71,10 @@ export const addToGuildController: RequestHandler = async (req, res) => {
|
||||
const member: IGuildMemberClient = {
|
||||
_id: { $oid: account._id.toString() },
|
||||
DisplayName: account.DisplayName,
|
||||
LastLogin: toMongoDate(account.LastLogin),
|
||||
Rank: 7,
|
||||
Status: 2
|
||||
};
|
||||
await addInventoryDataToFriendInfo(member);
|
||||
await fillInInventoryDataForGuildMember(member);
|
||||
res.json({ NewMember: member });
|
||||
} else if ("RequestMsg" in payload) {
|
||||
// Player applying to join a clan
|
||||
|
@ -28,7 +28,7 @@ export const artifactTransmutationController: RequestHandler = async (req, res)
|
||||
});
|
||||
|
||||
const rawRivenType = getRandomRawRivenType();
|
||||
const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType])!;
|
||||
const rivenType = getRandomElement(rivenRawToRealWeighted[rawRivenType]);
|
||||
const fingerprint = createVeiledRivenFingerprint(ExportUpgrades[rivenType]);
|
||||
|
||||
const upgradeIndex =
|
||||
|
@ -133,14 +133,7 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
|
||||
if (recipe.secretIngredientAction != "SIA_UNBRAND") {
|
||||
InventoryChanges = {
|
||||
...InventoryChanges,
|
||||
...(await addItem(
|
||||
inventory,
|
||||
recipe.resultType,
|
||||
recipe.num,
|
||||
false,
|
||||
undefined,
|
||||
pendingRecipe.TargetFingerprint
|
||||
))
|
||||
...(await addItem(inventory, recipe.resultType, recipe.num, false))
|
||||
};
|
||||
}
|
||||
await inventory.save();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
@ -17,7 +17,7 @@ export const claimLibraryDailyTaskRewardController: RequestHandler = async (req,
|
||||
}
|
||||
syndicate.Standing += rewardStanding;
|
||||
|
||||
addFusionPoints(inventory, 80 * rewardQuantity);
|
||||
inventory.FusionPoints += 80 * rewardQuantity;
|
||||
await inventory.save();
|
||||
|
||||
res.json({
|
||||
|
@ -1,41 +0,0 @@
|
||||
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,14 +1,8 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { Account } from "@/src/models/loginModel";
|
||||
import {
|
||||
deleteGuild,
|
||||
getGuildClient,
|
||||
giveClanKey,
|
||||
hasGuildPermission,
|
||||
removeDojoKeyItems
|
||||
} from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { deleteGuild, getGuildClient, hasGuildPermission, removeDojoKeyItems } from "@/src/services/guildService";
|
||||
import { addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, getAccountIdForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
import { GuildPermission } from "@/src/types/guildTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
@ -47,7 +41,14 @@ export const confirmGuildInvitationGetController: RequestHandler = async (req, r
|
||||
// Update inventory of new member
|
||||
const inventory = await getInventory(account._id.toString(), "GuildId LevelKeys Recipes");
|
||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||
giveClanKey(inventory, inventoryChanges);
|
||||
const recipeChanges = [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
];
|
||||
addRecipes(inventory, recipeChanges);
|
||||
combineInventoryChanges(inventoryChanges, { Recipes: recipeChanges });
|
||||
await inventory.save();
|
||||
|
||||
const guild = (await Guild.findById(req.query.clanId as string))!;
|
||||
@ -95,9 +96,14 @@ export const confirmGuildInvitationPostController: RequestHandler = async (req,
|
||||
await GuildMember.deleteMany({ accountId: guildMember.accountId, status: 1 });
|
||||
|
||||
// Update inventory of new member
|
||||
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId LevelKeys Recipes");
|
||||
const inventory = await getInventory(guildMember.accountId.toString(), "GuildId Recipes");
|
||||
inventory.GuildId = new Types.ObjectId(req.query.clanId as string);
|
||||
giveClanKey(inventory);
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
await inventory.save();
|
||||
|
||||
// Add join to clan log
|
||||
|
@ -2,7 +2,7 @@ import { toMongoDate } from "@/src/helpers/inventoryHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Guild } from "@/src/models/guildModel";
|
||||
import { checkClanAscensionHasRequiredContributors } from "@/src/services/guildService";
|
||||
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
import { Types } from "mongoose";
|
||||
@ -36,7 +36,7 @@ export const contributeGuildClassController: RequestHandler = async (req, res) =
|
||||
|
||||
// Either way, endo is given to the contributor.
|
||||
const inventory = await getInventory(accountId, "FusionPoints");
|
||||
addFusionPoints(inventory, guild.CeremonyEndo!);
|
||||
inventory.FusionPoints += guild.CeremonyEndo!;
|
||||
await inventory.save();
|
||||
|
||||
res.json({
|
||||
|
@ -2,9 +2,8 @@ import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { createUniqueClanName, getGuildClient, giveClanKey } from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { createUniqueClanName, getGuildClient } from "@/src/services/guildService";
|
||||
import { addRecipes, getInventory } from "@/src/services/inventoryService";
|
||||
|
||||
export const createGuildController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -27,15 +26,26 @@ export const createGuildController: RequestHandler = async (req, res) => {
|
||||
rank: 0
|
||||
});
|
||||
|
||||
const inventory = await getInventory(accountId, "GuildId LevelKeys Recipes");
|
||||
const inventory = await getInventory(accountId, "GuildId Recipes");
|
||||
inventory.GuildId = guild._id;
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
giveClanKey(inventory, inventoryChanges);
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
await inventory.save();
|
||||
|
||||
res.json({
|
||||
...(await getGuildClient(guild, accountId)),
|
||||
InventoryChanges: inventoryChanges
|
||||
InventoryChanges: {
|
||||
Recipes: [
|
||||
{
|
||||
ItemType: "/Lotus/Types/Keys/DojoKeyBlueprint",
|
||||
ItemCount: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -62,7 +62,14 @@ export const crewShipIdentifySalvageController: RequestHandler = async (req, res
|
||||
} satisfies IInnateDamageFingerprint)
|
||||
};
|
||||
}
|
||||
addEquipment(inventory, "CrewShipSalvagedWeapons", payload.ItemType, defaultOverwrites, inventoryChanges);
|
||||
addEquipment(
|
||||
inventory,
|
||||
"CrewShipSalvagedWeapons",
|
||||
payload.ItemType,
|
||||
undefined,
|
||||
inventoryChanges,
|
||||
defaultOverwrites
|
||||
);
|
||||
}
|
||||
|
||||
inventoryChanges.CrewShipRawSalvage = [
|
||||
|
@ -21,12 +21,10 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res)
|
||||
inventory.EntratiVaultCountResetDate = new Date(weekEnd);
|
||||
if (inventory.EntratiLabConquestUnlocked) {
|
||||
inventory.EntratiLabConquestUnlocked = 0;
|
||||
inventory.EntratiLabConquestCacheScoreMission = 0;
|
||||
inventory.EntratiLabConquestActiveFrameVariants = [];
|
||||
}
|
||||
if (inventory.EchoesHexConquestUnlocked) {
|
||||
inventory.EchoesHexConquestUnlocked = 0;
|
||||
inventory.EchoesHexConquestCacheScoreMission = 0;
|
||||
inventory.EchoesHexConquestActiveFrameVariants = [];
|
||||
inventory.EchoesHexConquestActiveStickers = [];
|
||||
}
|
||||
|
@ -104,14 +104,13 @@ export const focusController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
case FocusOperation.SentTrainingAmplifier: {
|
||||
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 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"
|
||||
]
|
||||
});
|
||||
const inventoryChanges = addEquipment(inventory, "OperatorAmps", request.StartingWeaponType, parts);
|
||||
occupySlot(inventory, InventorySlot.AMPS, false);
|
||||
await inventory.save();
|
||||
res.json((inventoryChanges.OperatorAmps as IEquipmentClient[])[0]);
|
||||
|
@ -1,84 +0,0 @@
|
||||
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[][]>;
|
||||
}
|
@ -18,7 +18,6 @@ export const getAllianceController: RequestHandler = async (req, res) => {
|
||||
res.end();
|
||||
};
|
||||
|
||||
// POST request since U27
|
||||
/*interface IGetAllianceRequest {
|
||||
memberCount: number;
|
||||
clanLeaderName: string;
|
||||
|
@ -1,54 +1,15 @@
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { Friendship } from "@/src/models/friendModel";
|
||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "@/src/services/friendService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IFriendInfo } from "@/src/types/friendTypes";
|
||||
import { Request, RequestHandler, Response } from "express";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
// POST with {} instead of GET as of 38.5.0
|
||||
export const getFriendsController: RequestHandler = async (req: Request, res: Response) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const response: IGetFriendsResponse = {
|
||||
Current: [],
|
||||
IncomingFriendRequests: [],
|
||||
OutgoingFriendRequests: []
|
||||
};
|
||||
const [internalFriendships, externalFriendships] = await Promise.all([
|
||||
Friendship.find({ owner: accountId }),
|
||||
Friendship.find({ friend: accountId }, "owner Note")
|
||||
]);
|
||||
for (const externalFriendship of externalFriendships) {
|
||||
if (!internalFriendships.find(x => x.friend.equals(externalFriendship.owner))) {
|
||||
response.IncomingFriendRequests.push({
|
||||
_id: toOid(externalFriendship.owner),
|
||||
Note: externalFriendship.Note
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const internalFriendship of internalFriendships) {
|
||||
const friendInfo: IFriendInfo = {
|
||||
_id: toOid(internalFriendship.friend)
|
||||
};
|
||||
if (externalFriendships.find(x => x.owner.equals(internalFriendship.friend))) {
|
||||
response.Current.push(friendInfo);
|
||||
} else {
|
||||
response.OutgoingFriendRequests.push(friendInfo);
|
||||
}
|
||||
}
|
||||
const promises: Promise<void>[] = [];
|
||||
for (const arr of Object.values(response)) {
|
||||
for (const friendInfo of arr) {
|
||||
promises.push(addAccountDataToFriendInfo(friendInfo));
|
||||
promises.push(addInventoryDataToFriendInfo(friendInfo));
|
||||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
res.json(response);
|
||||
const getFriendsController = (_request: Request, response: Response): void => {
|
||||
response.writeHead(200, {
|
||||
//Connection: "keep-alive",
|
||||
//"Content-Encoding": "gzip",
|
||||
"Content-Type": "text/html",
|
||||
// charset: "UTF - 8",
|
||||
"Content-Length": "3"
|
||||
});
|
||||
response.end(Buffer.from([0x7b, 0x7d, 0x0a]));
|
||||
};
|
||||
|
||||
// interface IGetFriendsResponse {
|
||||
// Current: IFriendInfo[];
|
||||
// IncomingFriendRequests: IFriendInfo[];
|
||||
// OutgoingFriendRequests: IFriendInfo[];
|
||||
// }
|
||||
type IGetFriendsResponse = Record<"Current" | "IncomingFriendRequests" | "OutgoingFriendRequests", IFriendInfo[]>;
|
||||
export { getFriendsController };
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { Account, Ignore } from "@/src/models/loginModel";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IFriendInfo } from "@/src/types/friendTypes";
|
||||
import { IFriendInfo } from "@/src/types/guildTypes";
|
||||
import { parallelForeach } from "@/src/utils/async-utils";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
|
@ -2,24 +2,17 @@ import { RequestHandler } from "express";
|
||||
import { config } from "@/src/services/configService";
|
||||
import allShipFeatures from "@/static/fixed_responses/allShipFeatures.json";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { getShip } from "@/src/services/shipService";
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { IGetShipResponse } from "@/src/types/shipTypes";
|
||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||
import { IPersonalRooms } from "@/src/types/personalRoomsTypes";
|
||||
import { getLoadout } from "@/src/services/loadoutService";
|
||||
|
||||
export const getShipController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const personalRoomsDb = await getPersonalRooms(accountId);
|
||||
|
||||
// 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 personalRooms = personalRoomsDb.toJSON<IPersonalRooms>();
|
||||
const loadout = await getLoadout(accountId);
|
||||
const ship = await getShip(personalRoomsDb.activeShipId, "ShipAttachments SkinFlavourItem");
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Account } from "@/src/models/loginModel";
|
||||
import { areFriends } from "@/src/services/friendService";
|
||||
import { createMessage } from "@/src/services/inboxService";
|
||||
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
@ -31,11 +30,8 @@ export const giftingController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
|
||||
// Cannot gift to players who have gifting disabled.
|
||||
const senderAccount = await getAccountForRequest(req);
|
||||
if (
|
||||
inventory.Settings?.GiftMode == "GIFT_MODE_NONE" ||
|
||||
(inventory.Settings?.GiftMode == "GIFT_MODE_FRIENDS" && !(await areFriends(account._id, senderAccount._id)))
|
||||
) {
|
||||
// TODO: Also consider GIFT_MODE_FRIENDS once friends are implemented
|
||||
if (inventory.Settings?.GiftMode == "GIFT_MODE_NONE") {
|
||||
res.status(400).send("17").end();
|
||||
return;
|
||||
}
|
||||
@ -44,6 +40,7 @@ export const giftingController: RequestHandler = async (req, res) => {
|
||||
// TODO: Cannot gift archwing items to players that have not completed the archwing quest. (Code 7)
|
||||
// TODO: Cannot gift necramechs to players that have not completed heart of deimos. (Code 20)
|
||||
|
||||
const senderAccount = await getAccountForRequest(req);
|
||||
const senderInventory = await getInventory(
|
||||
senderAccount._id.toString(),
|
||||
"PremiumCredits PremiumCreditsFree ActiveAvatarImageType GiftsRemaining"
|
||||
|
@ -441,9 +441,16 @@ const finishComponentRepair = (
|
||||
const inventoryChanges = {
|
||||
...(category == "CrewShipWeaponSkins"
|
||||
? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint)
|
||||
: addEquipment(inventory, category, salvageItem.ItemType, {
|
||||
UpgradeFingerprint: salvageItem.UpgradeFingerprint
|
||||
})),
|
||||
: addEquipment(
|
||||
inventory,
|
||||
category,
|
||||
salvageItem.ItemType,
|
||||
undefined,
|
||||
{},
|
||||
{
|
||||
UpgradeFingerprint: salvageItem.UpgradeFingerprint
|
||||
}
|
||||
)),
|
||||
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false)
|
||||
};
|
||||
|
||||
|
@ -11,10 +11,8 @@ import {
|
||||
import { getAccountForRequest, getAccountFromSuffixedName, getSuffixedName } from "@/src/services/loginService";
|
||||
import { addItems, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { ExportFlavour } from "warframe-public-export-plus";
|
||||
import { ExportFlavour, ExportGear } from "warframe-public-export-plus";
|
||||
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) => {
|
||||
const { deleteId, lastMessage: latestClientMessageId, messageId } = req.query;
|
||||
@ -29,10 +27,10 @@ export const inboxController: RequestHandler = async (req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
await deleteMessageRead(parseOid(deleteId as string));
|
||||
await deleteMessageRead(deleteId as string);
|
||||
res.status(200).end();
|
||||
} else if (messageId) {
|
||||
const message = await getMessage(parseOid(messageId as string));
|
||||
const message = await getMessage(messageId as string);
|
||||
message.r = true;
|
||||
await message.save();
|
||||
|
||||
@ -50,8 +48,8 @@ export const inboxController: RequestHandler = async (req, res) => {
|
||||
await addItems(
|
||||
inventory,
|
||||
attachmentItems.map(attItem => ({
|
||||
ItemType: isStoreItem(attItem) ? fromStoreItem(attItem) : attItem,
|
||||
ItemCount: 1
|
||||
ItemType: attItem,
|
||||
ItemCount: attItem in ExportGear ? (ExportGear[attItem].purchaseQuantity ?? 1) : 1
|
||||
})),
|
||||
inventoryChanges
|
||||
);
|
||||
@ -101,7 +99,7 @@ export const inboxController: RequestHandler = async (req, res) => {
|
||||
await createNewEventMessages(req);
|
||||
const messages = await Inbox.find({ ownerId: accountId }).sort({ date: 1 });
|
||||
|
||||
const latestClientMessage = messages.find(m => m._id.toString() === parseOid(latestClientMessageId as string));
|
||||
const latestClientMessage = messages.find(m => m._id.toString() === latestClientMessageId);
|
||||
|
||||
if (!latestClientMessage) {
|
||||
logger.debug(`this should only happen after DeleteAllRead `);
|
||||
@ -124,11 +122,3 @@ export const inboxController: RequestHandler = async (req, res) => {
|
||||
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;
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountForRequest } from "@/src/services/loginService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { config } from "@/src/services/configService";
|
||||
import allDialogue from "@/static/fixed_responses/allDialogue.json";
|
||||
@ -18,22 +18,15 @@ import {
|
||||
addMiscItems,
|
||||
allDailyAffiliationKeys,
|
||||
cleanupInventory,
|
||||
createLibraryDailyTask,
|
||||
generateRewardSeed
|
||||
createLibraryDailyTask
|
||||
} from "@/src/services/inventoryService";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||
import { Types } from "mongoose";
|
||||
import { isNemesisCompatibleWithVersion } from "@/src/helpers/nemesisHelpers";
|
||||
import { version_compare } from "@/src/services/worldStateService";
|
||||
import { getPersonalRooms } from "@/src/services/personalRoomsService";
|
||||
import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes";
|
||||
import { Ship } from "@/src/models/shipModel";
|
||||
|
||||
export const inventoryController: RequestHandler = async (request, response) => {
|
||||
const account = await getAccountForRequest(request);
|
||||
const accountId = await getAccountIdForRequest(request);
|
||||
|
||||
const inventory = await Inventory.findOne({ accountOwnerId: account._id });
|
||||
const inventory = await Inventory.findOne({ accountOwnerId: accountId });
|
||||
|
||||
if (!inventory) {
|
||||
response.status(400).json({ error: "inventory was undefined" });
|
||||
@ -94,7 +87,7 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
||||
cleanupInventory(inventory);
|
||||
|
||||
inventory.NextRefill = new Date((Math.trunc(Date.now() / 86400000) + 1) * 86400000);
|
||||
//await inventory.save();
|
||||
await inventory.save();
|
||||
}
|
||||
|
||||
if (
|
||||
@ -103,43 +96,23 @@ export const inventoryController: RequestHandler = async (request, response) =>
|
||||
new Date() >= inventory.InfestedFoundry.AbilityOverrideUnlockCooldown
|
||||
) {
|
||||
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, account.BuildLabel)
|
||||
);
|
||||
response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query));
|
||||
};
|
||||
|
||||
export const getInventoryResponse = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
xpBasedLevelCapDisabled: boolean,
|
||||
buildLabel: string | undefined
|
||||
xpBasedLevelCapDisabled: boolean
|
||||
): Promise<IInventoryClient> => {
|
||||
const [inventoryWithLoadOutPresets, ships] = await Promise.all([
|
||||
inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>("LoadOutPresets"),
|
||||
Ship.find({ ShipOwnerId: inventory.accountOwnerId })
|
||||
]);
|
||||
const inventoryResponse = inventoryWithLoadOutPresets.toJSON<IInventoryClient>();
|
||||
inventoryResponse.Ships = ships.map(x => x.toJSON<IShipInventory>());
|
||||
const inventoryWithLoadOutPresets = await inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>(
|
||||
"LoadOutPresets"
|
||||
);
|
||||
const inventoryWithLoadOutPresetsAndShips = await inventoryWithLoadOutPresets.populate<{ Ships: IShipInventory }>(
|
||||
"Ships"
|
||||
);
|
||||
const inventoryResponse = inventoryWithLoadOutPresetsAndShips.toJSON<IInventoryClient>();
|
||||
|
||||
if (config.infiniteCredits) {
|
||||
inventoryResponse.RegularCredits = 999999999;
|
||||
@ -301,26 +274,11 @@ export const getInventoryResponse = async (
|
||||
}
|
||||
|
||||
// Omitting this field so opening the navigation resyncs the inventory which is more desirable for typical usage.
|
||||
inventoryResponse.LastInventorySync = undefined;
|
||||
//inventoryResponse.LastInventorySync = toOid(new Types.ObjectId());
|
||||
|
||||
// Set 2FA enabled so trading post can be used
|
||||
inventoryResponse.HWIDProtectEnabled = true;
|
||||
|
||||
// Fix nemesis for older versions
|
||||
if (
|
||||
inventoryResponse.Nemesis &&
|
||||
buildLabel &&
|
||||
!isNemesisCompatibleWithVersion(inventoryResponse.Nemesis, buildLabel)
|
||||
) {
|
||||
inventoryResponse.Nemesis = undefined;
|
||||
}
|
||||
|
||||
if (buildLabel && version_compare(buildLabel, "2018.02.22.14.34") < 0) {
|
||||
const personalRoomsDb = await getPersonalRooms(inventory.accountOwnerId.toString());
|
||||
const personalRooms = personalRoomsDb.toJSON<IPersonalRoomsClient>();
|
||||
inventoryResponse.Ship = personalRooms.Ship;
|
||||
}
|
||||
|
||||
return inventoryResponse;
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,6 @@ import { Account } from "@/src/models/loginModel";
|
||||
import { createAccount, isCorrectPassword, isNameTaken } from "@/src/services/loginService";
|
||||
import { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "@/src/types/loginTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { version_compare } from "@/src/services/worldStateService";
|
||||
|
||||
export const loginController: RequestHandler = async (request, response) => {
|
||||
const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object
|
||||
@ -22,11 +21,7 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
|
||||
const myAddress = request.host.indexOf("warframe.com") == -1 ? request.host : config.myAddress;
|
||||
|
||||
if (
|
||||
!account &&
|
||||
((config.autoCreateAccount && loginRequest.ClientType != "webui") ||
|
||||
loginRequest.ClientType == "webui-register")
|
||||
) {
|
||||
if (!account && config.autoCreateAccount && loginRequest.ClientType != "webui") {
|
||||
try {
|
||||
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
|
||||
let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja";
|
||||
@ -41,15 +36,13 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
email: loginRequest.email,
|
||||
password: loginRequest.password,
|
||||
DisplayName: name,
|
||||
CountryCode: loginRequest.lang?.toUpperCase() ?? "EN",
|
||||
ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType,
|
||||
CountryCode: loginRequest.lang.toUpperCase(),
|
||||
ClientType: loginRequest.ClientType,
|
||||
CrossPlatformAllowed: true,
|
||||
ForceLogoutVersion: 0,
|
||||
ConsentNeeded: false,
|
||||
TrackedSettings: [],
|
||||
Nonce: nonce,
|
||||
BuildLabel: buildLabel,
|
||||
LastLogin: new Date()
|
||||
Nonce: nonce
|
||||
});
|
||||
logger.debug("created new account");
|
||||
response.json(createLoginResponse(myAddress, newAccount, buildLabel));
|
||||
@ -66,11 +59,6 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginRequest.ClientType == "webui-register") {
|
||||
response.status(400).json({ error: "account already exists" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCorrectPassword(loginRequest.password, account.password)) {
|
||||
response.status(400).json({ error: "incorrect login data" });
|
||||
return;
|
||||
@ -83,18 +71,13 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
}
|
||||
} else {
|
||||
if (account.Nonce && account.ClientType != "webui" && !account.Dropped && !loginRequest.kick) {
|
||||
// U17 seems to handle "nonce still set" like a login failure.
|
||||
if (version_compare(buildLabel, "2015.12.05.18.07") >= 0) {
|
||||
response.status(400).send({ error: "nonce still set" });
|
||||
return;
|
||||
}
|
||||
response.status(400).json({ error: "nonce still set" });
|
||||
return;
|
||||
}
|
||||
|
||||
account.ClientType = loginRequest.ClientType;
|
||||
account.Nonce = nonce;
|
||||
account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN";
|
||||
account.BuildLabel = buildLabel;
|
||||
account.LastLogin = new Date();
|
||||
account.CountryCode = loginRequest.lang.toUpperCase();
|
||||
}
|
||||
await account.save();
|
||||
|
||||
@ -102,48 +85,25 @@ export const loginController: RequestHandler = async (request, response) => {
|
||||
};
|
||||
|
||||
const createLoginResponse = (myAddress: string, account: IDatabaseAccountJson, buildLabel: string): ILoginResponse => {
|
||||
const resp: ILoginResponse = {
|
||||
return {
|
||||
id: account.id,
|
||||
DisplayName: account.DisplayName,
|
||||
CountryCode: account.CountryCode,
|
||||
ClientType: account.ClientType,
|
||||
CrossPlatformAllowed: account.CrossPlatformAllowed,
|
||||
ForceLogoutVersion: account.ForceLogoutVersion,
|
||||
AmazonAuthToken: account.AmazonAuthToken,
|
||||
AmazonRefreshToken: account.AmazonRefreshToken,
|
||||
ConsentNeeded: account.ConsentNeeded,
|
||||
TrackedSettings: account.TrackedSettings,
|
||||
Nonce: account.Nonce,
|
||||
BuildLabel: buildLabel
|
||||
Groups: [],
|
||||
IRC: config.myIrcAddresses ?? [myAddress],
|
||||
platformCDNs: [`https://${myAddress}/`],
|
||||
HUB: `https://${myAddress}/api/`,
|
||||
NRS: config.NRS,
|
||||
DTLS: 99,
|
||||
BuildLabel: buildLabel,
|
||||
MatchmakingBuildId: buildConfig.matchmakingBuildId
|
||||
};
|
||||
if (version_compare(buildLabel, "2015.02.13.10.41") >= 0) {
|
||||
resp.NRS = config.NRS;
|
||||
}
|
||||
if (version_compare(buildLabel, "2015.05.14.16.29") >= 0) {
|
||||
// U17 and up
|
||||
resp.IRC = config.myIrcAddresses ?? [myAddress];
|
||||
}
|
||||
if (version_compare(buildLabel, "2018.11.08.14.45") >= 0) {
|
||||
// U24 and up
|
||||
resp.ConsentNeeded = account.ConsentNeeded;
|
||||
resp.TrackedSettings = account.TrackedSettings;
|
||||
}
|
||||
if (version_compare(buildLabel, "2019.08.29.20.01") >= 0) {
|
||||
// U25.7 and up
|
||||
resp.ForceLogoutVersion = account.ForceLogoutVersion;
|
||||
}
|
||||
if (version_compare(buildLabel, "2019.10.31.22.42") >= 0) {
|
||||
// U26 and up
|
||||
resp.Groups = [];
|
||||
}
|
||||
if (version_compare(buildLabel, "2021.04.13.19.58") >= 0) {
|
||||
resp.DTLS = 99;
|
||||
}
|
||||
if (version_compare(buildLabel, "2022.04.29.12.53") >= 0) {
|
||||
resp.ClientType = account.ClientType;
|
||||
}
|
||||
if (version_compare(buildLabel, "2022.09.06.19.24") >= 0) {
|
||||
resp.CrossPlatformAllowed = account.CrossPlatformAllowed;
|
||||
resp.HUB = `https://${myAddress}/api/`;
|
||||
resp.MatchmakingBuildId = buildConfig.matchmakingBuildId;
|
||||
}
|
||||
if (version_compare(buildLabel, "2023.04.25.23.40") >= 0) {
|
||||
resp.platformCDNs = [`https://${myAddress}/`];
|
||||
}
|
||||
return resp;
|
||||
};
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { getAccountForRequest } from "@/src/services/loginService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes";
|
||||
import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService";
|
||||
import { generateRewardSeed, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventoryResponse } from "./inventoryController";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
|
||||
|
||||
/*
|
||||
**** INPUT ****
|
||||
@ -49,11 +48,11 @@ import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
|
||||
*/
|
||||
//move credit calc in here, return MissionRewards: [] if no reward info
|
||||
export const missionInventoryUpdateController: RequestHandler = async (req, res): Promise<void> => {
|
||||
const account = await getAccountForRequest(req);
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const missionReport = getJSONfromString<IMissionInventoryUpdateRequest>((req.body as string).toString());
|
||||
logger.debug("mission report:", missionReport);
|
||||
|
||||
const inventory = await getInventory(account._id.toString());
|
||||
const inventory = await getInventory(accountId);
|
||||
const firstCompletion = missionReport.SortieId
|
||||
? inventory.CompletedSorties.indexOf(missionReport.SortieId) == -1
|
||||
: false;
|
||||
@ -63,11 +62,8 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
||||
missionReport.MissionStatus !== "GS_SUCCESS" &&
|
||||
!(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId)
|
||||
) {
|
||||
if (missionReport.EndOfMatchUpload) {
|
||||
inventory.RewardSeed = generateRewardSeed();
|
||||
}
|
||||
await inventory.save();
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true, account.BuildLabel);
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||
res.json({
|
||||
InventoryJson: JSON.stringify(inventoryResponse),
|
||||
MissionRewards: []
|
||||
@ -75,20 +71,11 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
MissionRewards,
|
||||
inventoryChanges,
|
||||
credits,
|
||||
AffiliationMods,
|
||||
SyndicateXPItemReward,
|
||||
ConquestCompletedMissionsCount
|
||||
} = await addMissionRewards(inventory, missionReport, firstCompletion);
|
||||
const { MissionRewards, inventoryChanges, credits, AffiliationMods, SyndicateXPItemReward } =
|
||||
await addMissionRewards(inventory, missionReport, firstCompletion);
|
||||
|
||||
if (missionReport.EndOfMatchUpload) {
|
||||
inventory.RewardSeed = generateRewardSeed();
|
||||
}
|
||||
await inventory.save();
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true, account.BuildLabel);
|
||||
const inventoryResponse = await getInventoryResponse(inventory, true);
|
||||
|
||||
//TODO: figure out when to send inventory. it is needed for many cases.
|
||||
res.json({
|
||||
@ -99,9 +86,8 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
|
||||
...inventoryUpdates,
|
||||
//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);
|
||||
AffiliationMods
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -17,7 +17,7 @@ import { getDefaultUpgrades } from "@/src/services/itemDataService";
|
||||
import { modularWeaponTypes } from "@/src/helpers/modularWeaponHelper";
|
||||
import { IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { getRandomInt } from "@/src/services/rngService";
|
||||
import { ExportSentinels, ExportWeapons, IDefaultUpgrade } from "warframe-public-export-plus";
|
||||
import { ExportSentinels, IDefaultUpgrade } from "warframe-public-export-plus";
|
||||
import { Status } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
|
||||
interface IModularCraftRequest {
|
||||
@ -36,9 +36,7 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
const inventory = await getInventory(accountId);
|
||||
|
||||
let defaultUpgrades: IDefaultUpgrade[] | undefined;
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
||||
ModularParts: data.Parts
|
||||
};
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {};
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
if (category == "KubrowPets") {
|
||||
const traits = {
|
||||
@ -140,20 +138,8 @@ export const modularWeaponCraftingController: RequestHandler = async (req, res)
|
||||
} else {
|
||||
defaultUpgrades = getDefaultUpgrades(data.Parts);
|
||||
}
|
||||
|
||||
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);
|
||||
addEquipment(inventory, category, data.WeaponType, data.Parts, inventoryChanges, defaultOverwrites);
|
||||
combineInventoryChanges(
|
||||
inventoryChanges,
|
||||
occupySlot(inventory, productCategoryToInventoryBin(category)!, !!data.isWebUi)
|
||||
|
@ -21,11 +21,7 @@ import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||
const partTypeToParts: Record<string, string[]> = {};
|
||||
for (const [uniqueName, data] of Object.entries(ExportWeapons)) {
|
||||
if (
|
||||
data.partType &&
|
||||
data.premiumPrice &&
|
||||
!data.excludeFromCodex // exclude pvp variants
|
||||
) {
|
||||
if (data.partType && data.premiumPrice) {
|
||||
partTypeToParts[data.partType] ??= [];
|
||||
partTypeToParts[data.partType].push(uniqueName);
|
||||
}
|
||||
@ -45,18 +41,24 @@ export const modularWeaponSaleController: RequestHandler = async (req, res) => {
|
||||
const defaultUpgrades = getDefaultUpgrades(weaponInfo.ModularParts);
|
||||
const configs = applyDefaultUpgrades(inventory, defaultUpgrades);
|
||||
const inventoryChanges: IInventoryChanges = {
|
||||
...addEquipment(inventory, category, weaponInfo.ItemType, {
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
|
||||
ItemName: payload.ItemName,
|
||||
Configs: configs,
|
||||
ModularParts: weaponInfo.ModularParts,
|
||||
Polarity: [
|
||||
{
|
||||
Slot: payload.PolarizeSlot,
|
||||
Value: payload.PolarizeValue
|
||||
}
|
||||
]
|
||||
}),
|
||||
...addEquipment(
|
||||
inventory,
|
||||
category,
|
||||
weaponInfo.ItemType,
|
||||
weaponInfo.ModularParts,
|
||||
{},
|
||||
{
|
||||
Features: EquipmentFeatures.DOUBLE_CAPACITY | EquipmentFeatures.GILDED,
|
||||
ItemName: payload.ItemName,
|
||||
Configs: configs,
|
||||
Polarity: [
|
||||
{
|
||||
Slot: payload.PolarizeSlot,
|
||||
Value: payload.PolarizeValue
|
||||
}
|
||||
]
|
||||
}
|
||||
),
|
||||
...occupySlot(inventory, productCategoryToInventoryBin(category)!, true),
|
||||
...updateCurrency(inventory, weaponInfo.PremiumPrice, true)
|
||||
};
|
||||
@ -141,7 +143,7 @@ const getModularWeaponSale = (
|
||||
getItemType: (parts: string[]) => string
|
||||
): IModularWeaponSaleInfo => {
|
||||
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;
|
||||
for (const part of parts) {
|
||||
partsCost += ExportWeapons[part].premiumPrice!;
|
||||
|
@ -2,12 +2,8 @@ import {
|
||||
consumeModCharge,
|
||||
encodeNemesisGuess,
|
||||
getInfNodes,
|
||||
getKnifeUpgrade,
|
||||
getNemesisPasscode,
|
||||
getNemesisPasscodeModTypes,
|
||||
getWeaponsForManifest,
|
||||
IKnifeResponse,
|
||||
showdownNodes
|
||||
IKnifeResponse
|
||||
} from "@/src/helpers/nemesisHelpers";
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
||||
@ -18,8 +14,6 @@ import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import {
|
||||
IInnateDamageFingerprint,
|
||||
IInventoryClient,
|
||||
INemesisClient,
|
||||
InventorySlot,
|
||||
IUpgradeClient,
|
||||
IWeaponSkinClient,
|
||||
@ -105,45 +99,50 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
||||
encodeNemesisGuess(guess[0], result1, guess[1], result2, guess[2], result3)
|
||||
);
|
||||
|
||||
// Increase antivirus if correct antivirus mod is installed
|
||||
// Increase antivirus
|
||||
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!;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
inventory.Nemesis!.HenchmenKilled += antivirusGain;
|
||||
if (inventory.Nemesis!.HenchmenKilled >= 100) {
|
||||
inventory.Nemesis!.HenchmenKilled = 100;
|
||||
inventory.Nemesis!.InfNodes = [
|
||||
{
|
||||
Node: "CrewBattleNode559",
|
||||
Influence: 1
|
||||
}
|
||||
];
|
||||
inventory.Nemesis!.Weakened = true;
|
||||
} else {
|
||||
inventory.Nemesis!.InfNodes = getInfNodes("FC_INFESTATION", 0);
|
||||
}
|
||||
inventory.Nemesis!.InfNodes = getInfNodes("FC_INFESTATION", 0);
|
||||
|
||||
await inventory.save();
|
||||
res.json(response);
|
||||
@ -171,7 +170,18 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
||||
|
||||
let weaponIdx = -1;
|
||||
if (body.target.Faction != "FC_INFESTATION") {
|
||||
const weapons = getWeaponsForManifest(body.target.manifest);
|
||||
let weapons: readonly string[];
|
||||
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);
|
||||
weaponIdx = initialWeaponIdx;
|
||||
do {
|
||||
@ -213,38 +223,6 @@ export const nemesisController: RequestHandler = async (req, res) => {
|
||||
res.json({
|
||||
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 {
|
||||
logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
|
||||
throw new Error(`unknown nemesis mode: ${String(req.query.mode)}`);
|
||||
@ -296,19 +274,48 @@ interface INemesisRequiemRequest {
|
||||
guess: number; // grn/crp: 4 bits | coda: 3x 4 bits
|
||||
position: number; // grn/crp: 0-2 | coda: 0
|
||||
// knife field provided for coda only
|
||||
knife?: IKnife;
|
||||
knife?: {
|
||||
Item: IEquipmentClient;
|
||||
Skins: IWeaponSkinClient[];
|
||||
ModSlot: number;
|
||||
CustSlot: number;
|
||||
AttachedUpgrades: IUpgradeClient[];
|
||||
HiddenWhenHolstered: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// interface INemesisWeakenRequest {
|
||||
// target: INemesisClient;
|
||||
// knife: IKnife;
|
||||
// }
|
||||
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"
|
||||
];
|
||||
|
||||
interface IKnife {
|
||||
Item: IEquipmentClient;
|
||||
Skins: IWeaponSkinClient[];
|
||||
ModSlot: number;
|
||||
CustSlot: number;
|
||||
AttachedUpgrades: IUpgradeClient[];
|
||||
HiddenWhenHolstered: boolean;
|
||||
}
|
||||
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"
|
||||
];
|
||||
|
@ -37,7 +37,6 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
const deco = component.Decos.find(x => x._id.equals(request.MoveId))!;
|
||||
deco.Pos = request.Pos;
|
||||
deco.Rot = request.Rot;
|
||||
deco.Scale = request.Scale;
|
||||
} else {
|
||||
const deco =
|
||||
component.Decos[
|
||||
@ -46,7 +45,6 @@ export const placeDecoInComponentController: RequestHandler = async (req, res) =
|
||||
Type: request.Type,
|
||||
Pos: request.Pos,
|
||||
Rot: request.Rot,
|
||||
Scale: request.Scale,
|
||||
Name: request.Name,
|
||||
Sockets: request.Sockets
|
||||
}) - 1
|
||||
@ -115,9 +113,9 @@ interface IPlaceDecoInComponentRequest {
|
||||
Type: string;
|
||||
Pos: number[];
|
||||
Rot: number[];
|
||||
Scale?: number;
|
||||
Name?: string;
|
||||
Sockets?: number;
|
||||
Scale?: number; // only provided alongside MoveId and seems to always be 1
|
||||
MoveId?: string;
|
||||
ShipDeco?: boolean;
|
||||
VaultDeco?: boolean;
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
// Basic shim handling action=sync to login on U21
|
||||
export const questControlController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
const quests: IQuestState[] = [];
|
||||
for (const quest of inventory.QuestKeys) {
|
||||
quests.push({
|
||||
quest: quest.ItemType,
|
||||
state: 3 // COMPLETE
|
||||
});
|
||||
}
|
||||
res.json({
|
||||
QuestState: quests
|
||||
});
|
||||
};
|
||||
|
||||
interface IQuestState {
|
||||
quest: string;
|
||||
state: number;
|
||||
task?: string;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { Friendship } from "@/src/models/friendModel";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IOid } from "@/src/types/commonTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const removeFriendGetController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
if (req.query.all) {
|
||||
const [internalFriendships, externalFriendships] = await Promise.all([
|
||||
Friendship.find({ owner: accountId }, "friend"),
|
||||
Friendship.find({ friend: accountId }, "owner")
|
||||
]);
|
||||
const promises: Promise<void>[] = [];
|
||||
const friends: IOid[] = [];
|
||||
for (const externalFriendship of externalFriendships) {
|
||||
if (!internalFriendships.find(x => x.friend.equals(externalFriendship.owner))) {
|
||||
promises.push(Friendship.deleteOne({ _id: externalFriendship._id }) as unknown as Promise<void>);
|
||||
friends.push(toOid(externalFriendship.owner));
|
||||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
res.json({
|
||||
Friends: friends
|
||||
});
|
||||
} else {
|
||||
const friendId = req.query.friendId as string;
|
||||
await Promise.all([
|
||||
Friendship.deleteOne({ owner: accountId, friend: friendId }),
|
||||
Friendship.deleteOne({ owner: friendId, friend: accountId })
|
||||
]);
|
||||
res.json({
|
||||
Friends: [{ $oid: friendId } satisfies IOid]
|
||||
});
|
||||
}
|
||||
};
|
@ -4,6 +4,7 @@ import { addEmailItem, getInventory, updateCurrency } from "@/src/services/inven
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { ICompletedDialogue, IDialogueDatabase } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
import { logger } from "@/src/utils/logger";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
@ -26,33 +27,33 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
const dialogue = getDialogue(inventory, request.DialogueName);
|
||||
dialogue.Rank = request.Rank;
|
||||
dialogue.Chemistry = request.Chemistry;
|
||||
dialogue.QueuedDialogues = request.QueuedDialogues;
|
||||
for (const bool of request.Booleans) {
|
||||
dialogue.Booleans.push(bool);
|
||||
if (bool == "LizzieShawzin") {
|
||||
await addEmailItem(
|
||||
inventory,
|
||||
"/Lotus/Types/Items/EmailItems/LizzieShawzinSkinEmailItem",
|
||||
inventoryChanges
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const bool of request.ResetBooleans) {
|
||||
const index = dialogue.Booleans.findIndex(x => x == bool);
|
||||
if (index != -1) {
|
||||
dialogue.Booleans.splice(index, 1);
|
||||
}
|
||||
}
|
||||
for (const info of request.OtherDialogueInfos) {
|
||||
const otherDialogue = getDialogue(inventory, info.Dialogue);
|
||||
if (info.Tag != "") {
|
||||
otherDialogue.QueuedDialogues.push(info.Tag);
|
||||
}
|
||||
otherDialogue.Chemistry += info.Value; // unsure
|
||||
}
|
||||
if (request.Data) {
|
||||
dialogue.QueuedDialogues = request.QueuedDialogues;
|
||||
for (const bool of request.Booleans) {
|
||||
dialogue.Booleans.push(bool);
|
||||
if (bool == "LizzieShawzin") {
|
||||
await addEmailItem(
|
||||
inventory,
|
||||
"/Lotus/Types/Items/EmailItems/LizzieShawzinSkinEmailItem",
|
||||
inventoryChanges
|
||||
);
|
||||
}
|
||||
}
|
||||
for (const bool of request.ResetBooleans) {
|
||||
const index = dialogue.Booleans.findIndex(x => x == bool);
|
||||
if (index != -1) {
|
||||
dialogue.Booleans.splice(index, 1);
|
||||
}
|
||||
}
|
||||
dialogue.Completed.push(request.Data);
|
||||
dialogue.AvailableDate = new Date(tomorrowAt0Utc);
|
||||
for (const info of request.OtherDialogueInfos) {
|
||||
const otherDialogue = getDialogue(inventory, info.Dialogue);
|
||||
if (info.Tag != "") {
|
||||
otherDialogue.QueuedDialogues.push(info.Tag);
|
||||
}
|
||||
otherDialogue.Chemistry += info.Value; // unsure
|
||||
}
|
||||
await inventory.save();
|
||||
res.json({
|
||||
InventoryChanges: inventoryChanges,
|
||||
@ -73,7 +74,7 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
|
||||
AvailableGiftDate: { $date: { $numberLong: tomorrowAt0Utc.toString() } }
|
||||
});
|
||||
} else {
|
||||
res.end();
|
||||
logger.error(`saveDialogue request not fully handled: ${String(req.body)}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -8,8 +8,7 @@ import {
|
||||
addConsumables,
|
||||
freeUpSlot,
|
||||
combineInventoryChanges,
|
||||
addCrewShipRawSalvage,
|
||||
addFusionPoints
|
||||
addCrewShipRawSalvage
|
||||
} from "@/src/services/inventoryService";
|
||||
import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
@ -45,10 +44,7 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
if (payload.Items.SpaceGuns || payload.Items.SpaceMelee) {
|
||||
requiredFields.add(InventorySlot.SPACEWEAPONS);
|
||||
}
|
||||
if (payload.Items.MechSuits) {
|
||||
requiredFields.add(InventorySlot.MECHSUITS);
|
||||
}
|
||||
if (payload.Items.Sentinels || payload.Items.SentinelWeapons || payload.Items.MoaPets) {
|
||||
if (payload.Items.Sentinels || payload.Items.SentinelWeapons) {
|
||||
requiredFields.add(InventorySlot.SENTINELS);
|
||||
}
|
||||
if (payload.Items.OperatorAmps) {
|
||||
@ -73,7 +69,7 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
if (payload.SellCurrency == "SC_RegularCredits") {
|
||||
inventory.RegularCredits += payload.SellPrice;
|
||||
} else if (payload.SellCurrency == "SC_FusionPoints") {
|
||||
addFusionPoints(inventory, payload.SellPrice);
|
||||
inventory.FusionPoints += payload.SellPrice;
|
||||
} else if (payload.SellCurrency == "SC_PrimeBucks") {
|
||||
addMiscItems(inventory, [
|
||||
{
|
||||
@ -139,12 +135,6 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
freeUpSlot(inventory, InventorySlot.SPACEWEAPONS);
|
||||
});
|
||||
}
|
||||
if (payload.Items.MechSuits) {
|
||||
payload.Items.MechSuits.forEach(sellItem => {
|
||||
inventory.MechSuits.pull({ _id: sellItem.String });
|
||||
freeUpSlot(inventory, InventorySlot.MECHSUITS);
|
||||
});
|
||||
}
|
||||
if (payload.Items.Sentinels) {
|
||||
payload.Items.Sentinels.forEach(sellItem => {
|
||||
inventory.Sentinels.pull({ _id: sellItem.String });
|
||||
@ -157,12 +147,6 @@ export const sellController: RequestHandler = async (req, res) => {
|
||||
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) {
|
||||
payload.Items.OperatorAmps.forEach(sellItem => {
|
||||
inventory.OperatorAmps.pull({ _id: sellItem.String });
|
||||
@ -294,10 +278,8 @@ interface ISellRequest {
|
||||
SpaceSuits?: ISellItem[];
|
||||
SpaceGuns?: ISellItem[];
|
||||
SpaceMelee?: ISellItem[];
|
||||
MechSuits?: ISellItem[];
|
||||
Sentinels?: ISellItem[];
|
||||
SentinelWeapons?: ISellItem[];
|
||||
MoaPets?: ISellItem[];
|
||||
OperatorAmps?: ISellItem[];
|
||||
Hoverboards?: ISellItem[];
|
||||
Drones?: ISellItem[];
|
||||
|
@ -1,30 +0,0 @@
|
||||
import { getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { Friendship } from "@/src/models/friendModel";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const setFriendNoteController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const payload = getJSONfromString<ISetFriendNoteRequest>(String(req.body));
|
||||
const friendship = await Friendship.findOne({ owner: accountId, friend: payload.FriendId }, "Note Favorite");
|
||||
if (friendship) {
|
||||
if ("Note" in payload) {
|
||||
friendship.Note = payload.Note;
|
||||
} else {
|
||||
friendship.Favorite = payload.Favorite;
|
||||
}
|
||||
await friendship.save();
|
||||
}
|
||||
res.json({
|
||||
Id: payload.FriendId,
|
||||
SetNote: "Note" in payload,
|
||||
Note: friendship?.Note,
|
||||
Favorite: friendship?.Favorite
|
||||
});
|
||||
};
|
||||
|
||||
interface ISetFriendNoteRequest {
|
||||
FriendId: string;
|
||||
Note?: string;
|
||||
Favorite?: boolean;
|
||||
}
|
@ -2,7 +2,6 @@ import { Alliance, Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { hasGuildPermissionEx } from "@/src/services/guildService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
|
||||
import { version_compare } from "@/src/services/worldStateService";
|
||||
import { GuildPermission, ILongMOTD } from "@/src/types/guildTypes";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
@ -56,9 +55,5 @@ export const setGuildMotdController: RequestHandler = async (req, res) => {
|
||||
await guild.save();
|
||||
}
|
||||
|
||||
if (!account.BuildLabel || version_compare(account.BuildLabel, "2020.03.24.20.24") > 0) {
|
||||
res.json({ IsLongMOTD, MOTD });
|
||||
} else {
|
||||
res.send(MOTD).end();
|
||||
}
|
||||
res.json({ IsLongMOTD, MOTD });
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ export const setShipFavouriteLoadoutController: RequestHandler = async (req, res
|
||||
throw new Error(`unexpected BootLocation: ${body.BootLocation}`);
|
||||
}
|
||||
await personalRooms.save();
|
||||
res.json(body);
|
||||
res.json({});
|
||||
};
|
||||
|
||||
interface ISetShipFavouriteLoadoutRequest {
|
||||
|
@ -41,7 +41,7 @@ export const startRecipeController: RequestHandler = async (req, res) => {
|
||||
];
|
||||
|
||||
for (let i = 0; i != recipe.ingredients.length; ++i) {
|
||||
if (startRecipeRequest.Ids[i] && startRecipeRequest.Ids[i][0] != "/") {
|
||||
if (startRecipeRequest.Ids[i]) {
|
||||
const category = ExportWeapons[recipe.ingredients[i].ItemType].productCategory;
|
||||
if (category != "LongGuns" && category != "Pistols" && category != "Melee") {
|
||||
throw new Error(`unexpected equipment ingredient type: ${category}`);
|
||||
|
@ -6,9 +6,6 @@ import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
|
||||
import { addMiscItems, combineInventoryChanges, getInventory, updateCurrency } from "@/src/services/inventoryService";
|
||||
import { IInventoryChanges } from "@/src/types/purchaseTypes";
|
||||
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) => {
|
||||
const accountId = await getAccountIdForRequest(request);
|
||||
@ -77,14 +74,10 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
|
||||
if (!isStoreItem(rewardType)) {
|
||||
rewardType = toStoreItem(rewardType);
|
||||
}
|
||||
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);
|
||||
combineInventoryChanges(
|
||||
res.InventoryChanges,
|
||||
(await handleStoreItemAcquisition(rewardType, inventory, reward.itemCount)).InventoryChanges
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,12 @@
|
||||
import { RequestHandler } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addFusionPoints, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
|
||||
export const addCurrencyController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
const inventory = await getInventory(accountId);
|
||||
const request = req.body as IAddCurrencyRequest;
|
||||
const inventory = await getInventory(accountId, request.currency);
|
||||
if (request.currency == "FusionPoints") {
|
||||
addFusionPoints(inventory, request.delta);
|
||||
} else {
|
||||
inventory[request.currency] += request.delta;
|
||||
}
|
||||
inventory[request.currency] += request.delta;
|
||||
await inventory.save();
|
||||
res.end();
|
||||
};
|
||||
|
@ -7,7 +7,7 @@ export const addItemsController: RequestHandler = async (req, res) => {
|
||||
const requests = req.body as IAddItemRequest[];
|
||||
const inventory = await getInventory(accountId);
|
||||
for (const request of requests) {
|
||||
await addItem(inventory, request.ItemType, request.ItemCount, true, undefined, undefined, true);
|
||||
await addItem(inventory, request.ItemType, request.ItemCount, true);
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
|
@ -1,44 +0,0 @@
|
||||
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,4 +1,4 @@
|
||||
import { applyClientEquipmentUpdates, getInventory } from "@/src/services/inventoryService";
|
||||
import { addGearExpByCategory, getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
@ -20,7 +20,7 @@ export const addXpController: RequestHandler = async (req, res) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
applyClientEquipmentUpdates(inventory, gear, category as TEquipmentKey);
|
||||
addGearExpByCategory(inventory, gear, category as TEquipmentKey);
|
||||
}
|
||||
await inventory.save();
|
||||
res.end();
|
||||
|
@ -10,7 +10,6 @@ import { Stats } from "@/src/models/statsModel";
|
||||
import { GuildMember } from "@/src/models/guildModel";
|
||||
import { Leaderboard } from "@/src/models/leaderboardModel";
|
||||
import { deleteGuild } from "@/src/services/guildService";
|
||||
import { Friendship } from "@/src/models/friendModel";
|
||||
|
||||
export const deleteAccountController: RequestHandler = async (req, res) => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -23,8 +22,6 @@ export const deleteAccountController: RequestHandler = async (req, res) => {
|
||||
|
||||
await Promise.all([
|
||||
Account.deleteOne({ _id: accountId }),
|
||||
Friendship.deleteMany({ owner: accountId }),
|
||||
Friendship.deleteMany({ friend: accountId }),
|
||||
GuildMember.deleteMany({ accountId: accountId }),
|
||||
Ignore.deleteMany({ ignorer: accountId }),
|
||||
Ignore.deleteMany({ ignoree: accountId }),
|
||||
|
@ -1,18 +1,15 @@
|
||||
import { AllianceMember, Guild, GuildMember } from "@/src/models/guildModel";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
|
||||
import { RequestHandler } from "express";
|
||||
|
||||
export const getAccountInfoController: RequestHandler = async (req, res) => {
|
||||
const account = await getAccountForRequest(req);
|
||||
const inventory = await getInventory(account._id.toString(), "QuestKeys");
|
||||
const info: IAccountInfo = {
|
||||
DisplayName: account.DisplayName,
|
||||
IsAdministrator: isAdministrator(account),
|
||||
CompletedVorsPrize: !!inventory.QuestKeys.find(
|
||||
x => x.ItemType == "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"
|
||||
)?.Completed
|
||||
DisplayName: account.DisplayName
|
||||
};
|
||||
if (isAdministrator(account)) {
|
||||
info.IsAdministrator = true;
|
||||
}
|
||||
const guildMember = await GuildMember.findOne({ accountId: account._id, status: 0 }, "guildId rank");
|
||||
if (guildMember) {
|
||||
const guild = (await Guild.findById(guildMember.guildId, "Ranks AllianceId"))!;
|
||||
@ -34,8 +31,7 @@ export const getAccountInfoController: RequestHandler = async (req, res) => {
|
||||
|
||||
interface IAccountInfo {
|
||||
DisplayName: string;
|
||||
IsAdministrator: boolean;
|
||||
CompletedVorsPrize: boolean;
|
||||
IsAdministrator?: boolean;
|
||||
GuildId?: string;
|
||||
GuildPermissions?: number;
|
||||
GuildRank?: number;
|
||||
|
@ -20,7 +20,6 @@ import {
|
||||
TRelicQuality
|
||||
} from "warframe-public-export-plus";
|
||||
import archonCrystalUpgrades from "@/static/fixed_responses/webuiArchonCrystalUpgrades.json";
|
||||
import allIncarnons from "@/static/fixed_responses/allIncarnonList.json";
|
||||
|
||||
interface ListedItem {
|
||||
uniqueName: string;
|
||||
@ -33,29 +32,6 @@ interface ListedItem {
|
||||
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> = {
|
||||
VPQ_BRONZE: "",
|
||||
VPQ_SILVER: " [Flawless]",
|
||||
@ -65,28 +41,23 @@ const relicQualitySuffixes: Record<TRelicQuality, string> = {
|
||||
|
||||
const getItemListsController: RequestHandler = (req, response) => {
|
||||
const lang = getDict(typeof req.query.lang == "string" ? req.query.lang : "en");
|
||||
const res: ItemLists = {
|
||||
archonCrystalUpgrades,
|
||||
uniqueLevelCaps: ExportMisc.uniqueLevelCaps,
|
||||
Suits: [],
|
||||
LongGuns: [],
|
||||
Melee: [],
|
||||
ModularParts: [],
|
||||
Pistols: [],
|
||||
Sentinels: [],
|
||||
SentinelWeapons: [],
|
||||
SpaceGuns: [],
|
||||
SpaceMelee: [],
|
||||
SpaceSuits: [],
|
||||
MechSuits: [],
|
||||
miscitems: [],
|
||||
Syndicates: [],
|
||||
OperatorAmps: [],
|
||||
QuestKeys: [],
|
||||
KubrowPets: [],
|
||||
EvolutionProgress: [],
|
||||
mods: []
|
||||
};
|
||||
const res: Record<string, ListedItem[]> = {};
|
||||
res.Suits = [];
|
||||
res.LongGuns = [];
|
||||
res.Melee = [];
|
||||
res.ModularParts = [];
|
||||
res.Pistols = [];
|
||||
res.Sentinels = [];
|
||||
res.SentinelWeapons = [];
|
||||
res.SpaceGuns = [];
|
||||
res.SpaceMelee = [];
|
||||
res.SpaceSuits = [];
|
||||
res.MechSuits = [];
|
||||
res.miscitems = [];
|
||||
res.Syndicates = [];
|
||||
res.OperatorAmps = [];
|
||||
res.QuestKeys = [];
|
||||
res.KubrowPets = [];
|
||||
for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
|
||||
res[item.productCategory].push({
|
||||
uniqueName,
|
||||
@ -95,7 +66,7 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
});
|
||||
}
|
||||
for (const [uniqueName, item] of Object.entries(ExportSentinels)) {
|
||||
if (item.productCategory == "Sentinels" || item.productCategory == "KubrowPets") {
|
||||
if (item.productCategory != "SpecialItems") {
|
||||
res[item.productCategory].push({
|
||||
uniqueName,
|
||||
name: getString(item.name, lang),
|
||||
@ -144,28 +115,12 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
let name = getString(item.name, lang);
|
||||
if ("dissectionParts" in item) {
|
||||
name = getString("/Lotus/Language/Fish/FishDisplayName", lang).split("|FISH_NAME|").join(name);
|
||||
if (item.syndicateTag == "CetusSyndicate") {
|
||||
if (uniqueName.indexOf("Large") != -1) {
|
||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeLargeAbbrev", 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));
|
||||
}
|
||||
if (uniqueName.indexOf("Large") != -1) {
|
||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeLargeAbbrev", lang));
|
||||
} else if (uniqueName.indexOf("Medium") != -1) {
|
||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeMediumAbbrev", lang));
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
name = name.split("|FISH_SIZE|").join(getString("/Lotus/Language/Fish/FishSizeSmallAbbrev", lang));
|
||||
}
|
||||
}
|
||||
if (
|
||||
@ -229,6 +184,7 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
});
|
||||
}
|
||||
|
||||
res.mods = [];
|
||||
for (const [uniqueName, upgrade] of Object.entries(ExportUpgrades)) {
|
||||
const mod: ListedItem = {
|
||||
uniqueName,
|
||||
@ -286,14 +242,12 @@ const getItemListsController: RequestHandler = (req, response) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const uniqueName of allIncarnons) {
|
||||
res.EvolutionProgress.push({
|
||||
uniqueName,
|
||||
name: getString(getItemName(uniqueName) || "", lang)
|
||||
});
|
||||
}
|
||||
|
||||
response.json(res);
|
||||
response.json({
|
||||
archonCrystalUpgrades,
|
||||
uniqueLevelCaps: ExportMisc.uniqueLevelCaps,
|
||||
...res
|
||||
});
|
||||
};
|
||||
|
||||
export { getItemListsController };
|
||||
|
@ -1,33 +0,0 @@
|
||||
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;
|
||||
}[];
|
@ -18,71 +18,62 @@ import {
|
||||
ITypeXPItem
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { RequestHandler } from "express";
|
||||
import { catBreadHash, getJSONfromString } from "@/src/helpers/stringHelpers";
|
||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||
import { ExportCustoms, ExportDojoRecipes } from "warframe-public-export-plus";
|
||||
import { IStatsClient } from "@/src/types/statTypes";
|
||||
import { toStoreItem } from "@/src/services/itemDataService";
|
||||
import { FlattenMaps } from "mongoose";
|
||||
|
||||
const getProfileViewingDataByPlayerIdImpl = async (playerId: string): Promise<IProfileViewingData | undefined> => {
|
||||
const account = await Account.findById(playerId, "DisplayName");
|
||||
if (!account) {
|
||||
return;
|
||||
}
|
||||
const inventory = (await Inventory.findOne({ accountOwnerId: account._id }))!;
|
||||
|
||||
const result: IPlayerProfileViewingDataResult = {
|
||||
AccountId: toOid(account._id),
|
||||
DisplayName: account.DisplayName,
|
||||
PlayerLevel: inventory.PlayerLevel,
|
||||
LoadOutInventory: {
|
||||
WeaponSkins: [],
|
||||
XPInfo: inventory.XPInfo
|
||||
},
|
||||
PlayerSkills: inventory.PlayerSkills,
|
||||
ChallengeProgress: inventory.ChallengeProgress,
|
||||
DeathMarks: inventory.DeathMarks,
|
||||
Harvestable: inventory.Harvestable,
|
||||
DeathSquadable: inventory.DeathSquadable,
|
||||
Created: toMongoDate(inventory.Created),
|
||||
MigratedToConsole: false,
|
||||
Missions: inventory.Missions,
|
||||
Affiliations: inventory.Affiliations,
|
||||
DailyFocus: inventory.DailyFocus,
|
||||
Wishlist: inventory.Wishlist,
|
||||
Alignment: inventory.Alignment
|
||||
};
|
||||
await populateLoadout(inventory, result);
|
||||
if (inventory.GuildId) {
|
||||
const guild = (await Guild.findById(inventory.GuildId, "Name Tier XP Class Emblem"))!;
|
||||
populateGuild(guild, result);
|
||||
}
|
||||
for (const key of allDailyAffiliationKeys) {
|
||||
result[key] = inventory[key];
|
||||
}
|
||||
|
||||
const stats = (await Stats.findOne({ accountOwnerId: account._id }))!.toJSON<Partial<TStatsDatabaseDocument>>();
|
||||
delete stats._id;
|
||||
delete stats.__v;
|
||||
delete stats.accountOwnerId;
|
||||
|
||||
return {
|
||||
Results: [result],
|
||||
TechProjects: [],
|
||||
XpComponents: [],
|
||||
//XpCacheExpiryDate, some IMongoDate in the future, no clue what it's for
|
||||
Stats: stats
|
||||
};
|
||||
};
|
||||
|
||||
export const getProfileViewingDataGetController: RequestHandler = async (req, res) => {
|
||||
export const getProfileViewingDataController: RequestHandler = async (req, res) => {
|
||||
if (req.query.playerId) {
|
||||
const data = await getProfileViewingDataByPlayerIdImpl(req.query.playerId as string);
|
||||
if (data) {
|
||||
res.json(data);
|
||||
} else {
|
||||
const account = await Account.findById(req.query.playerId as string, "DisplayName");
|
||||
if (!account) {
|
||||
res.status(409).send("Could not find requested account");
|
||||
return;
|
||||
}
|
||||
const inventory = (await Inventory.findOne({ accountOwnerId: account._id }))!;
|
||||
|
||||
const result: IPlayerProfileViewingDataResult = {
|
||||
AccountId: toOid(account._id),
|
||||
DisplayName: account.DisplayName,
|
||||
PlayerLevel: inventory.PlayerLevel,
|
||||
LoadOutInventory: {
|
||||
WeaponSkins: [],
|
||||
XPInfo: inventory.XPInfo
|
||||
},
|
||||
PlayerSkills: inventory.PlayerSkills,
|
||||
ChallengeProgress: inventory.ChallengeProgress,
|
||||
DeathMarks: inventory.DeathMarks,
|
||||
Harvestable: inventory.Harvestable,
|
||||
DeathSquadable: inventory.DeathSquadable,
|
||||
Created: toMongoDate(inventory.Created),
|
||||
MigratedToConsole: false,
|
||||
Missions: inventory.Missions,
|
||||
Affiliations: inventory.Affiliations,
|
||||
DailyFocus: inventory.DailyFocus,
|
||||
Wishlist: inventory.Wishlist,
|
||||
Alignment: inventory.Alignment
|
||||
};
|
||||
await populateLoadout(inventory, result);
|
||||
if (inventory.GuildId) {
|
||||
const guild = (await Guild.findById(inventory.GuildId, "Name Tier XP Class Emblem"))!;
|
||||
populateGuild(guild, result);
|
||||
}
|
||||
for (const key of allDailyAffiliationKeys) {
|
||||
result[key] = inventory[key];
|
||||
}
|
||||
|
||||
const stats = (await Stats.findOne({ accountOwnerId: account._id }))!.toJSON<Partial<TStatsDatabaseDocument>>();
|
||||
delete stats._id;
|
||||
delete stats.__v;
|
||||
delete stats.accountOwnerId;
|
||||
|
||||
res.json({
|
||||
Results: [result],
|
||||
TechProjects: [],
|
||||
XpComponents: [],
|
||||
//XpCacheExpiryDate, some IMongoDate in the future, no clue what it's for
|
||||
Stats: stats
|
||||
});
|
||||
} else if (req.query.guildId) {
|
||||
const guild = await Guild.findById(req.query.guildId, "Name Tier XP Class Emblem TechProjects ClaimedXP");
|
||||
if (!guild) {
|
||||
@ -179,28 +170,6 @@ export const getProfileViewingDataGetController: RequestHandler = async (req, re
|
||||
}
|
||||
};
|
||||
|
||||
// For old versions, this was an authenticated POST request.
|
||||
interface IGetProfileViewingDataRequest {
|
||||
AccountId: string;
|
||||
}
|
||||
export const getProfileViewingDataPostController: RequestHandler = async (req, res) => {
|
||||
const payload = getJSONfromString<IGetProfileViewingDataRequest>(String(req.body));
|
||||
const data = await getProfileViewingDataByPlayerIdImpl(payload.AccountId);
|
||||
if (data) {
|
||||
res.json(data);
|
||||
} else {
|
||||
res.status(409).send("Could not find requested account");
|
||||
}
|
||||
};
|
||||
|
||||
interface IProfileViewingData {
|
||||
Results: IPlayerProfileViewingDataResult[];
|
||||
TechProjects: [];
|
||||
XpComponents: [];
|
||||
//XpCacheExpiryDate, some IMongoDate in the future, no clue what it's for
|
||||
Stats: FlattenMaps<Partial<TStatsDatabaseDocument>>;
|
||||
}
|
||||
|
||||
interface IPlayerProfileViewingDataResult extends Partial<IDailyAffiliations> {
|
||||
AccountId: IOid;
|
||||
DisplayName: string;
|
||||
|
@ -27,15 +27,7 @@ const viewController: RequestHandler = async (req, res) => {
|
||||
for (const type of Object.keys(ExportEnemies.avatars)) {
|
||||
if (!scans.has(type)) scans.add(type);
|
||||
}
|
||||
|
||||
// Take any existing scans and also set them to 9999
|
||||
if (responseJson.Scans) {
|
||||
for (const scan of responseJson.Scans) {
|
||||
scans.add(scan.type);
|
||||
}
|
||||
}
|
||||
responseJson.Scans = [];
|
||||
|
||||
responseJson.Scans ??= [];
|
||||
for (const type of scans) {
|
||||
responseJson.Scans.push({ type: type, scans: 9999 });
|
||||
}
|
||||
|
@ -48,8 +48,7 @@ const toDatabaseAccount = (createAccount: IAccountCreation): IDatabaseAccountReq
|
||||
CrossPlatformAllowed: true,
|
||||
ForceLogoutVersion: 0,
|
||||
TrackedSettings: [],
|
||||
Nonce: 0,
|
||||
LastLogin: new Date()
|
||||
Nonce: 0
|
||||
} satisfies IDatabaseAccountRequiredFields;
|
||||
};
|
||||
|
||||
|
@ -10,10 +10,6 @@ export const toMongoDate = (date: Date): IMongoDate => {
|
||||
return { $date: { $numberLong: date.getTime().toString() } };
|
||||
};
|
||||
|
||||
export const fromMongoDate = (date: IMongoDate): Date => {
|
||||
return new Date(parseInt(date.$date.$numberLong));
|
||||
};
|
||||
|
||||
export const kubrowWeights: Record<TRarity, number> = {
|
||||
COMMON: 6,
|
||||
UNCOMMON: 4,
|
||||
|
@ -1,14 +1,11 @@
|
||||
import { ExportRegions, ExportWarframes } from "warframe-public-export-plus";
|
||||
import { IInfNode, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { getRewardAtPercentage, SRng } from "@/src/services/rngService";
|
||||
import { ExportRegions } from "warframe-public-export-plus";
|
||||
import { IInfNode } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { 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, version_compare } from "../services/worldStateService";
|
||||
import { fromStoreItem, toStoreItem } from "../services/itemDataService";
|
||||
import { createMessage } from "../services/inboxService";
|
||||
import { addMods } from "../services/inventoryService";
|
||||
|
||||
export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
|
||||
const infNodes = [];
|
||||
@ -25,7 +22,7 @@ export const getInfNodes = (faction: string, rank: number): IInfNode[] => {
|
||||
value.missionIndex != 42 && // not face off
|
||||
value.name.indexOf("1999NodeI") == -1 && // not stage defence
|
||||
value.name.indexOf("1999NodeJ") == -1 && // not lich bounty
|
||||
!isArchwingMission(value)
|
||||
value.name.indexOf("Archwing") == -1
|
||||
) {
|
||||
//console.log(dict_en[value.name]);
|
||||
infNodes.push({ Node: key, Influence: 1 });
|
||||
@ -40,59 +37,17 @@ const systemIndexes: Record<string, number[]> = {
|
||||
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.
|
||||
export const getNemesisPasscode = (nemesis: { fp: bigint; Faction: string }): number[] => {
|
||||
const rng = new SRng(nemesis.fp);
|
||||
const choices = [0, 1, 2, 3, 5, 6, 7];
|
||||
let choiceIndex = rng.randomInt(0, choices.length - 1);
|
||||
const passcode = [choices[choiceIndex]];
|
||||
const passcode = [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]);
|
||||
passcode.push(rng.randomInt(0, 7));
|
||||
passcode.push(rng.randomInt(0, 7));
|
||||
}
|
||||
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,
|
||||
@ -123,31 +78,6 @@ export interface IKnifeResponse {
|
||||
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,
|
||||
@ -198,205 +128,3 @@ export const consumeModCharge = (
|
||||
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": // >= 35.6.0
|
||||
return kuvaLichVersionSixWeapons;
|
||||
case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree": // >= 35.6.0
|
||||
case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour": // >= 37.0.0
|
||||
return corpusVersionThreeWeapons;
|
||||
}
|
||||
throw new Error(`unknown nemesis manifest: ${manifest}`);
|
||||
};
|
||||
|
||||
export const isNemesisCompatibleWithVersion = (
|
||||
nemesis: { manifest: string; Faction: string },
|
||||
buildLabel: string
|
||||
): boolean => {
|
||||
// Anything below 35.6.0 is not going to be okay given our set of supported manifests.
|
||||
if (version_compare(buildLabel, "2024.05.15.11.07") < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nemesis.Faction == "FC_INFESTATION") {
|
||||
// Anything below 38.5.0 isn't gonna like an infested lich.
|
||||
if (version_compare(buildLabel, "2025.03.18.16.07") < 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (nemesis.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour") {
|
||||
// Anything below 37.0.0 isn't gonna know version 4, but version 3 is identical in terms of weapon choices, so we can spoof it to that.
|
||||
if (version_compare(buildLabel, "2024.10.01.11.03") < 0) {
|
||||
nemesis.manifest = "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
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 => {
|
||||
const challenge = getRandomElement(meta.availableChallenges!)!;
|
||||
const challenge = getRandomElement(meta.availableChallenges!);
|
||||
const fingerprintChallenge: IRivenChallenge = {
|
||||
Type: challenge.fullName,
|
||||
Progress: 0,
|
||||
@ -54,11 +54,11 @@ export const createVeiledRivenFingerprint = (meta: IUpgrade): IVeiledRivenFinger
|
||||
|
||||
export const createUnveiledRivenFingerprint = (meta: IUpgrade): IUnveiledRivenFingerprint => {
|
||||
const fingerprint: IUnveiledRivenFingerprint = {
|
||||
compat: getRandomElement(meta.compatibleItems!)!,
|
||||
compat: getRandomElement(meta.compatibleItems!),
|
||||
lim: 0,
|
||||
lvl: 0,
|
||||
lvlReq: getRandomInt(8, 16),
|
||||
pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"])!,
|
||||
pol: getRandomElement(["AP_ATTACK", "AP_DEFENSE", "AP_TACTIC"]),
|
||||
buffs: [],
|
||||
curses: []
|
||||
};
|
||||
@ -81,7 +81,7 @@ export const randomiseRivenStats = (meta: IUpgrade, fingerprint: IUnveiledRivenF
|
||||
if (Math.random() < 0.5) {
|
||||
const entry = getRandomElement(
|
||||
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) });
|
||||
}
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import { JSONParse } from "json-with-bigint";
|
||||
|
||||
export const getJSONfromString = <T>(str: string): T => {
|
||||
const jsonSubstring = str.substring(0, str.lastIndexOf("}") + 1);
|
||||
return JSONParse(jsonSubstring) as T;
|
||||
return JSONParse<T>(jsonSubstring);
|
||||
};
|
||||
|
||||
export const getSubstringFromKeyword = (str: string, keyword: string): string => {
|
||||
|
@ -17,11 +17,13 @@ import https from "https";
|
||||
import fs from "node:fs";
|
||||
import { app } from "./app";
|
||||
import mongoose from "mongoose";
|
||||
import { JSONStringify } from "json-with-bigint";
|
||||
import { Json, JSONStringify } from "json-with-bigint";
|
||||
import { validateConfig } from "@/src/services/configWatcherService";
|
||||
|
||||
// Patch JSON.stringify to work flawlessly with Bigints.
|
||||
JSON.stringify = JSONStringify;
|
||||
JSON.stringify = (obj: Exclude<Json, undefined>, _replacer?: unknown, space?: string | number): string => {
|
||||
return JSONStringify(obj, space);
|
||||
};
|
||||
|
||||
validateConfig();
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
import { IFriendship } from "@/src/types/friendTypes";
|
||||
import { model, Schema } from "mongoose";
|
||||
|
||||
const friendshipSchema = new Schema<IFriendship>({
|
||||
owner: { type: Schema.Types.ObjectId, required: true },
|
||||
friend: { type: Schema.Types.ObjectId, required: true },
|
||||
Note: String,
|
||||
Favorite: Boolean
|
||||
});
|
||||
|
||||
friendshipSchema.index({ owner: 1 });
|
||||
friendshipSchema.index({ friend: 1 });
|
||||
friendshipSchema.index({ owner: 1, friend: 1 }, { unique: true });
|
||||
|
||||
export const Friendship = model<IFriendship>("Friendship", friendshipSchema);
|
@ -23,7 +23,6 @@ const dojoDecoSchema = new Schema<IDojoDecoDatabase>({
|
||||
Type: String,
|
||||
Pos: [Number],
|
||||
Rot: [Number],
|
||||
Scale: Number,
|
||||
Name: String,
|
||||
Sockets: Number,
|
||||
RegularCredits: Number,
|
||||
|
@ -96,8 +96,7 @@ import {
|
||||
IInvasionProgressDatabase,
|
||||
IInvasionProgressClient,
|
||||
IAccolades,
|
||||
IHubNpcCustomization,
|
||||
ILotusCustomization
|
||||
IHubNpcCustomization
|
||||
} from "../../types/inventoryTypes/inventoryTypes";
|
||||
import { IOid } from "../../types/commonTypes";
|
||||
import {
|
||||
@ -391,8 +390,8 @@ MailboxSchema.set("toJSON", {
|
||||
|
||||
const DuviriInfoSchema = new Schema<IDuviriInfo>(
|
||||
{
|
||||
Seed: { type: BigInt, required: true },
|
||||
NumCompletions: { type: Number, required: true }
|
||||
Seed: Number,
|
||||
NumCompletions: { type: Number, default: 0 }
|
||||
},
|
||||
{
|
||||
_id: false,
|
||||
@ -781,26 +780,6 @@ const loreFragmentScansSchema = new Schema<ILoreFragmentScan>(
|
||||
{ _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>(
|
||||
{
|
||||
Progress: Number,
|
||||
@ -1055,8 +1034,6 @@ const pendingRecipeSchema = new Schema<IPendingRecipeDatabase>(
|
||||
{
|
||||
ItemType: String,
|
||||
CompletionDate: Date,
|
||||
TargetItemId: String,
|
||||
TargetFingerprint: String,
|
||||
LongGuns: { type: [EquipmentSchema], default: undefined },
|
||||
Pistols: { type: [EquipmentSchema], default: undefined },
|
||||
Melee: { type: [EquipmentSchema], default: undefined },
|
||||
@ -1148,15 +1125,15 @@ const CustomMarkersSchema = new Schema<ICustomMarkers>(
|
||||
const calenderProgressSchema = new Schema<ICalendarProgress>(
|
||||
{
|
||||
Version: { type: Number, default: 19 },
|
||||
Iteration: { type: Number, required: true },
|
||||
Iteration: { type: Number, default: 2 },
|
||||
YearProgress: {
|
||||
Upgrades: { type: [String], default: [] }
|
||||
Upgrades: { type: [] }
|
||||
},
|
||||
SeasonProgress: {
|
||||
SeasonType: { type: String, required: true },
|
||||
LastCompletedDayIdx: { type: Number, default: 0 },
|
||||
LastCompletedChallengeDayIdx: { type: Number, default: 0 },
|
||||
ActivatedChallenges: { type: [String], default: [] }
|
||||
SeasonType: String,
|
||||
LastCompletedDayIdx: { type: Number, default: -1 },
|
||||
LastCompletedChallengeDayIdx: { type: Number, default: -1 },
|
||||
ActivatedChallenges: []
|
||||
}
|
||||
},
|
||||
{ _id: false }
|
||||
@ -1278,11 +1255,11 @@ const nemesisSchema = new Schema<INemesisDatabase>(
|
||||
PrevOwners: Number,
|
||||
SecondInCommand: Boolean,
|
||||
Weakened: Boolean,
|
||||
InfNodes: { type: [infNodeSchema], default: undefined },
|
||||
InfNodes: [infNodeSchema],
|
||||
HenchmenKilled: Number,
|
||||
HintProgress: Number,
|
||||
Hints: { type: [Number], default: undefined },
|
||||
GuessHistory: { type: [Number], default: undefined },
|
||||
Hints: [Number],
|
||||
GuessHistory: [Number],
|
||||
MissionCount: Number,
|
||||
LastEnc: Number
|
||||
},
|
||||
@ -1399,7 +1376,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
//How many Gift do you have left*(gift spends the trade)
|
||||
GiftsRemaining: { type: Number, default: 8 },
|
||||
//Curent trade info Giving or Getting items
|
||||
//PendingTrades: [Schema.Types.Mixed],
|
||||
PendingTrades: [Schema.Types.Mixed],
|
||||
|
||||
//Syndicate currently being pledged to.
|
||||
SupportedSyndicate: String,
|
||||
@ -1449,7 +1426,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
KubrowPetEggs: [kubrowPetEggSchema],
|
||||
//Prints Cat(3 Prints)\Kubrow(2 Prints) Pets
|
||||
//KubrowPetPrints: [Schema.Types.Mixed],
|
||||
KubrowPetPrints: [Schema.Types.Mixed],
|
||||
|
||||
//Item for EquippedGear example:Scaner,LoadoutTechSummon etc
|
||||
Consumables: [typeCountSchema],
|
||||
@ -1495,7 +1472,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
//item like DojoKey or Boss missions key
|
||||
LevelKeys: [typeCountSchema],
|
||||
//Active quests
|
||||
//Quests: [Schema.Types.Mixed],
|
||||
Quests: [Schema.Types.Mixed],
|
||||
|
||||
//Cosmetics like profile glyphs\Kavasa Prime Kubrow Collar\Game Theme etc
|
||||
FlavourItems: [FlavourItemSchema],
|
||||
@ -1534,7 +1511,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
TauntHistory: { type: [tauntSchema], default: undefined },
|
||||
|
||||
//noShow2FA,VisitPrimeVault etc
|
||||
//WebFlags: Schema.Types.Mixed,
|
||||
WebFlags: Schema.Types.Mixed,
|
||||
//Id CompletedAlerts
|
||||
CompletedAlerts: [String],
|
||||
|
||||
@ -1554,7 +1531,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
//the color your clan requests like Items/Research/DojoColors/DojoColorPlainsB
|
||||
ActiveDojoColorResearch: String,
|
||||
|
||||
//SentientSpawnChanceBoosters: Schema.Types.Mixed,
|
||||
SentientSpawnChanceBoosters: Schema.Types.Mixed,
|
||||
|
||||
QualifyingInvasions: [invasionProgressSchema],
|
||||
FactionScores: [Number],
|
||||
@ -1589,10 +1566,10 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
// open location store like EidolonPlainsDiscoverable or OrbVallisCaveDiscoverable
|
||||
DiscoveredMarkers: [discoveredMarkerSchema],
|
||||
//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,
|
||||
//PersonalGoalProgress: [Schema.Types.Mixed],
|
||||
PersonalGoalProgress: [Schema.Types.Mixed],
|
||||
|
||||
//Setting interface Style
|
||||
ThemeStyle: String,
|
||||
@ -1622,13 +1599,13 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
LibraryActiveDailyTaskInfo: libraryDailyTaskInfoSchema,
|
||||
|
||||
//https://warframe.fandom.com/wiki/Invasion
|
||||
//InvasionChainProgress: [Schema.Types.Mixed],
|
||||
InvasionChainProgress: [Schema.Types.Mixed],
|
||||
|
||||
//CorpusLich or GrineerLich
|
||||
NemesisAbandonedRewards: { type: [String], default: [] },
|
||||
Nemesis: nemesisSchema,
|
||||
NemesisHistory: { type: [nemesisSchema], default: undefined },
|
||||
//LastNemesisAllySpawnTime: Schema.Types.Mixed,
|
||||
NemesisHistory: [Schema.Types.Mixed],
|
||||
LastNemesisAllySpawnTime: Schema.Types.Mixed,
|
||||
|
||||
//TradingRulesConfirmed,ShowFriendInvNotifications(Option->Social)
|
||||
Settings: settingsSchema,
|
||||
@ -1642,7 +1619,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
PlayerSkills: { type: playerSkillsSchema, default: {} },
|
||||
|
||||
//TradeBannedUntil data
|
||||
//TradeBannedUntil: Schema.Types.Mixed,
|
||||
TradeBannedUntil: Schema.Types.Mixed,
|
||||
|
||||
//https://warframe.fandom.com/wiki/Helminth
|
||||
InfestedFoundry: infestedFoundrySchema,
|
||||
@ -1651,7 +1628,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
//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
|
||||
LotusCustomization: { type: lotusCustomizationSchema, default: undefined },
|
||||
LotusCustomization: Schema.Types.Mixed,
|
||||
|
||||
//Progress+Rank+ItemType(ZarimanPumpShotgun)
|
||||
//https://warframe.fandom.com/wiki/Incarnon
|
||||
@ -1662,24 +1639,23 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
|
||||
|
||||
//Unknown and system
|
||||
DuviriInfo: DuviriInfoSchema,
|
||||
LastInventorySync: Schema.Types.ObjectId,
|
||||
Mailbox: MailboxSchema,
|
||||
HandlerPoints: Number,
|
||||
ChallengesFixVersion: { type: Number, default: 6 },
|
||||
PlayedParkourTutorial: Boolean,
|
||||
//ActiveLandscapeTraps: [Schema.Types.Mixed],
|
||||
//RepVotes: [Schema.Types.Mixed],
|
||||
//LeagueTickets: [Schema.Types.Mixed],
|
||||
ActiveLandscapeTraps: [Schema.Types.Mixed],
|
||||
RepVotes: [Schema.Types.Mixed],
|
||||
LeagueTickets: [Schema.Types.Mixed],
|
||||
HasContributedToDojo: Boolean,
|
||||
HWIDProtectEnabled: Boolean,
|
||||
LoadOutPresets: { type: Schema.Types.ObjectId, ref: "Loadout" },
|
||||
CurrentLoadOutIds: [oidSchema],
|
||||
RandomUpgradesIdentified: Number,
|
||||
BountyScore: Number,
|
||||
//ChallengeInstanceStates: [Schema.Types.Mixed],
|
||||
ChallengeInstanceStates: [Schema.Types.Mixed],
|
||||
RecentVendorPurchases: { type: [recentVendorPurchaseSchema], default: undefined },
|
||||
//Robotics: [Schema.Types.Mixed],
|
||||
//UsedDailyDeals: [Schema.Types.Mixed],
|
||||
Robotics: [Schema.Types.Mixed],
|
||||
UsedDailyDeals: [Schema.Types.Mixed],
|
||||
CollectibleSeries: { type: [collectibleEntrySchema], default: undefined },
|
||||
HasResetAccount: { type: Boolean, default: false },
|
||||
|
||||
@ -1760,9 +1736,6 @@ inventorySchema.set("toJSON", {
|
||||
sn: inventoryDatabase.LockedWeaponGroup.sn ? toOid(inventoryDatabase.LockedWeaponGroup.sn) : undefined
|
||||
};
|
||||
}
|
||||
if (inventoryDatabase.LastInventorySync) {
|
||||
inventoryResponse.LastInventorySync = toOid(inventoryDatabase.LastInventorySync);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -20,9 +20,7 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
|
||||
ConsentNeeded: { type: Boolean, required: true },
|
||||
TrackedSettings: { type: [String], default: [] },
|
||||
Nonce: { type: Number, default: 0 },
|
||||
BuildLabel: String,
|
||||
Dropped: Boolean,
|
||||
LastLogin: { type: Date, default: 0 },
|
||||
LatestEventMessageDate: { type: Date, default: 0 },
|
||||
LastLoginRewardDate: { type: Number, default: 0 },
|
||||
LoginDays: { type: Number, default: 1 }
|
||||
|
@ -1,19 +1,16 @@
|
||||
import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { toOid } from "@/src/helpers/inventoryHelpers";
|
||||
import { colorSchema } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import { IOrbiter, IPersonalRoomsDatabase, PersonalRoomsModelType } from "@/src/types/personalRoomsTypes";
|
||||
import {
|
||||
IFavouriteLoadoutDatabase,
|
||||
IGardeningDatabase,
|
||||
IGardening,
|
||||
IPlacedDecosDatabase,
|
||||
IPictureFrameInfo,
|
||||
IRoom,
|
||||
ITailorShopDatabase,
|
||||
IApartmentDatabase,
|
||||
IPlanterDatabase,
|
||||
IPlantDatabase,
|
||||
IPlantClient
|
||||
IApartmentDatabase
|
||||
} from "@/src/types/shipTypes";
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import { Schema, model } from "mongoose";
|
||||
|
||||
export const pictureFrameInfoSchema = new Schema<IPictureFrameInfo>(
|
||||
{
|
||||
@ -80,45 +77,15 @@ favouriteLoadoutSchema.set("toJSON", {
|
||||
}
|
||||
});
|
||||
|
||||
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 gardeningSchema = new Schema<IGardening>({
|
||||
Planters: [Schema.Types.Mixed] //TODO: add when implementing gardening
|
||||
});
|
||||
|
||||
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
|
||||
Gardening: gardeningSchema // TODO: ensure this is correct
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
@ -131,9 +98,7 @@ const apartmentDefault: IApartmentDatabase = {
|
||||
{ Name: "DuviriHallway", MaxCapacity: 1600 }
|
||||
],
|
||||
FavouriteLoadouts: [],
|
||||
Gardening: {
|
||||
Planters: []
|
||||
}
|
||||
Gardening: {}
|
||||
};
|
||||
|
||||
const orbiterSchema = new Schema<IOrbiter>(
|
||||
@ -153,18 +118,7 @@ const orbiterDefault: IOrbiter = {
|
||||
Features: ["/Lotus/Types/Items/ShipFeatureItems/EarthNavigationFeatureItem"], //TODO: potentially remove after missionstarting gear
|
||||
Rooms: [
|
||||
{ Name: "AlchemyRoom", MaxCapacity: 1600 },
|
||||
{
|
||||
Name: "BridgeRoom",
|
||||
MaxCapacity: 1600,
|
||||
PlacedDecos: [
|
||||
{
|
||||
Type: "/Lotus/Objects/Tenno/Props/Ships/LandCraftPlayerProps/ConclaveConsolePlayerShipDeco",
|
||||
Pos: [-30.082, -3.95954, -16.7913],
|
||||
Rot: [-135, 0, 0],
|
||||
_id: undefined as unknown as Types.ObjectId
|
||||
}
|
||||
]
|
||||
},
|
||||
{ Name: "BridgeRoom", MaxCapacity: 1600 },
|
||||
{ Name: "LisetRoom", MaxCapacity: 1000 },
|
||||
{ Name: "OperatorChamberRoom", MaxCapacity: 1600 },
|
||||
{ Name: "OutsideRoom", MaxCapacity: 1600 },
|
||||
|
@ -28,15 +28,17 @@ shipSchema.set("toJSON", {
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.ShipOwnerId;
|
||||
if (shipDatabase.ShipExteriorColors) {
|
||||
shipResponse.ShipExterior = {
|
||||
Colors: shipDatabase.ShipExteriorColors,
|
||||
ShipAttachments: shipDatabase.ShipAttachments,
|
||||
SkinFlavourItem: shipDatabase.SkinFlavourItem
|
||||
};
|
||||
|
||||
shipResponse.ShipExterior = {
|
||||
Colors: shipDatabase.ShipExteriorColors,
|
||||
ShipAttachments: shipDatabase.ShipAttachments,
|
||||
SkinFlavourItem: shipDatabase.SkinFlavourItem
|
||||
};
|
||||
delete shipDatabase.ShipExteriorColors;
|
||||
delete shipDatabase.ShipAttachments;
|
||||
delete shipDatabase.SkinFlavourItem;
|
||||
delete shipDatabase.ShipExteriorColors;
|
||||
delete shipDatabase.ShipAttachments;
|
||||
delete shipDatabase.SkinFlavourItem;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3,10 +3,8 @@ import { abandonLibraryDailyTaskController } from "@/src/controllers/api/abandon
|
||||
import { abortDojoComponentController } from "@/src/controllers/api/abortDojoComponentController";
|
||||
import { abortDojoComponentDestructionController } from "@/src/controllers/api/abortDojoComponentDestructionController";
|
||||
import { activateRandomModController } from "@/src/controllers/api/activateRandomModController";
|
||||
import { addFriendController } from "@/src/controllers/api/addFriendController";
|
||||
import { addFriendImageController } from "@/src/controllers/api/addFriendImageController";
|
||||
import { addIgnoredUserController } from "@/src/controllers/api/addIgnoredUserController";
|
||||
import { addPendingFriendController } from "@/src/controllers/api/addPendingFriendController";
|
||||
import { addToAllianceController } from "@/src/controllers/api/addToAllianceController";
|
||||
import { addToGuildController } from "@/src/controllers/api/addToGuildController";
|
||||
import { arcaneCommonController } from "@/src/controllers/api/arcaneCommonController";
|
||||
@ -21,7 +19,6 @@ import { claimCompletedRecipeController } from "@/src/controllers/api/claimCompl
|
||||
import { claimLibraryDailyTaskRewardController } from "@/src/controllers/api/claimLibraryDailyTaskRewardController";
|
||||
import { clearDialogueHistoryController } from "@/src/controllers/api/clearDialogueHistoryController";
|
||||
import { clearNewEpisodeRewardController } from "@/src/controllers/api/clearNewEpisodeRewardController";
|
||||
import { completeCalendarEventController } from "@/src/controllers/api/completeCalendarEventController";
|
||||
import { completeRandomModChallengeController } from "@/src/controllers/api/completeRandomModChallengeController";
|
||||
import { confirmAllianceInvitationController } from "@/src/controllers/api/confirmAllianceInvitationController";
|
||||
import { confirmGuildInvitationGetController, confirmGuildInvitationPostController } from "@/src/controllers/api/confirmGuildInvitationController";
|
||||
@ -50,7 +47,6 @@ import { findSessionsController } from "@/src/controllers/api/findSessionsContro
|
||||
import { fishmongerController } from "@/src/controllers/api/fishmongerController";
|
||||
import { focusController } from "@/src/controllers/api/focusController";
|
||||
import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController";
|
||||
import { gardeningController } from "@/src/controllers/api/gardeningController";
|
||||
import { genericUpdateController } from "@/src/controllers/api/genericUpdateController";
|
||||
import { getAllianceController } from "@/src/controllers/api/getAllianceController";
|
||||
import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController";
|
||||
@ -61,7 +57,6 @@ import { getGuildDojoController } from "@/src/controllers/api/getGuildDojoContro
|
||||
import { getGuildLogController } from "@/src/controllers/api/getGuildLogController";
|
||||
import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsersController";
|
||||
import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController";
|
||||
import { getProfileViewingDataPostController } from "@/src/controllers/dynamic/getProfileViewingDataController";
|
||||
import { getShipController } from "@/src/controllers/api/getShipController";
|
||||
import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
|
||||
import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController";
|
||||
@ -99,11 +94,9 @@ import { playerSkillsController } from "@/src/controllers/api/playerSkillsContro
|
||||
import { postGuildAdvertisementController } from "@/src/controllers/api/postGuildAdvertisementController";
|
||||
import { projectionManagerController } from "@/src/controllers/api/projectionManagerController";
|
||||
import { purchaseController } from "@/src/controllers/api/purchaseController";
|
||||
import { questControlController } from "@/src/controllers/api/questControlController";
|
||||
import { queueDojoComponentDestructionController } from "@/src/controllers/api/queueDojoComponentDestructionController";
|
||||
import { redeemPromoCodeController } from "@/src/controllers/api/redeemPromoCodeController";
|
||||
import { releasePetController } from "@/src/controllers/api/releasePetController";
|
||||
import { removeFriendGetController } from "@/src/controllers/api/removeFriendController";
|
||||
import { removeFromAllianceController } from "@/src/controllers/api/removeFromAllianceController";
|
||||
import { removeFromGuildController } from "@/src/controllers/api/removeFromGuildController";
|
||||
import { removeIgnoredUserController } from "@/src/controllers/api/removeIgnoredUserController";
|
||||
@ -123,7 +116,6 @@ import { setDojoComponentColorsController } from "@/src/controllers/api/setDojoC
|
||||
import { setDojoComponentMessageController } from "@/src/controllers/api/setDojoComponentMessageController";
|
||||
import { setDojoComponentSettingsController } from "@/src/controllers/api/setDojoComponentSettingsController";
|
||||
import { setEquippedInstrumentController } from "@/src/controllers/api/setEquippedInstrumentController";
|
||||
import { setFriendNoteController } from "@/src/controllers/api/setFriendNoteController";
|
||||
import { setGuildMotdController } from "@/src/controllers/api/setGuildMotdController";
|
||||
import { setHubNpcCustomizationsController } from "@/src/controllers/api/setHubNpcCustomizationsController";
|
||||
import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDecoInfoController";
|
||||
@ -166,7 +158,6 @@ apiRouter.get("/changeDojoRoot.php", changeDojoRootController);
|
||||
apiRouter.get("/changeGuildRank.php", changeGuildRankController);
|
||||
apiRouter.get("/checkDailyMissionBonus.php", checkDailyMissionBonusController);
|
||||
apiRouter.get("/claimLibraryDailyTaskReward.php", claimLibraryDailyTaskRewardController);
|
||||
apiRouter.get("/completeCalendarEvent.php", completeCalendarEventController);
|
||||
apiRouter.get("/confirmAllianceInvitation.php", confirmAllianceInvitationController);
|
||||
apiRouter.get("/confirmGuildInvitation.php", confirmGuildInvitationGetController);
|
||||
apiRouter.get("/credits.php", creditsController);
|
||||
@ -176,7 +167,6 @@ apiRouter.get("/deleteSession.php", deleteSessionController);
|
||||
apiRouter.get("/divvyAllianceVault.php", divvyAllianceVaultController);
|
||||
apiRouter.get("/dojo", dojoController);
|
||||
apiRouter.get("/drones.php", dronesController);
|
||||
apiRouter.get("/getAlliance.php", getAllianceController);
|
||||
apiRouter.get("/getDailyDealStockLevels.php", getDailyDealStockLevelsController);
|
||||
apiRouter.get("/getFriends.php", getFriendsController);
|
||||
apiRouter.get("/getGuild.php", getGuildController);
|
||||
@ -184,7 +174,6 @@ apiRouter.get("/getGuildContributions.php", getGuildContributionsController);
|
||||
apiRouter.get("/getGuildDojo.php", getGuildDojoController);
|
||||
apiRouter.get("/getGuildLog.php", getGuildLogController);
|
||||
apiRouter.get("/getIgnoredUsers.php", getIgnoredUsersController);
|
||||
apiRouter.get("/getMessages.php", inboxController); // unsure if this is correct, but needed for U17
|
||||
apiRouter.get("/getNewRewardSeed.php", getNewRewardSeedController);
|
||||
apiRouter.get("/getShip.php", getShipController);
|
||||
apiRouter.get("/getVendorInfo.php", getVendorInfoController);
|
||||
@ -198,9 +187,7 @@ apiRouter.get("/marketRecommendations.php", marketRecommendationsController);
|
||||
apiRouter.get("/marketSearchRecommendations.php", marketRecommendationsController);
|
||||
apiRouter.get("/modularWeaponSale.php", modularWeaponSaleController);
|
||||
apiRouter.get("/playedParkourTutorial.php", playedParkourTutorialController);
|
||||
apiRouter.get("/questControl.php", questControlController);
|
||||
apiRouter.get("/queueDojoComponentDestruction.php", queueDojoComponentDestructionController);
|
||||
apiRouter.get("/removeFriend.php", removeFriendGetController);
|
||||
apiRouter.get("/removeFromAlliance.php", removeFromAllianceController);
|
||||
apiRouter.get("/setActiveQuest.php", setActiveQuestController);
|
||||
apiRouter.get("/setActiveShip.php", setActiveShipController);
|
||||
@ -218,10 +205,8 @@ apiRouter.get("/updateSession.php", updateSessionGetController);
|
||||
// post
|
||||
apiRouter.post("/abortDojoComponent.php", abortDojoComponentController);
|
||||
apiRouter.post("/activateRandomMod.php", activateRandomModController);
|
||||
apiRouter.post("/addFriend.php", addFriendController);
|
||||
apiRouter.post("/addFriendImage.php", addFriendImageController);
|
||||
apiRouter.post("/addIgnoredUser.php", addIgnoredUserController);
|
||||
apiRouter.post("/addPendingFriend.php", addPendingFriendController);
|
||||
apiRouter.post("/addToAlliance.php", addToAllianceController);
|
||||
apiRouter.post("/addToGuild.php", addToGuildController);
|
||||
apiRouter.post("/arcaneCommon.php", arcaneCommonController);
|
||||
@ -232,7 +217,6 @@ apiRouter.post("/changeDojoRoot.php", changeDojoRootController);
|
||||
apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController);
|
||||
apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController);
|
||||
apiRouter.post("/clearNewEpisodeReward.php", clearNewEpisodeRewardController);
|
||||
apiRouter.post("/commitStoryModeDecision.php", (_req, res) => { res.end(); }); // U14 (maybe wanna actually unlock the ship features?)
|
||||
apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController);
|
||||
apiRouter.post("/confirmGuildInvitation.php", confirmGuildInvitationPostController);
|
||||
apiRouter.post("/contributeGuildClass.php", contributeGuildClassController);
|
||||
@ -254,12 +238,10 @@ apiRouter.post("/findSessions.php", findSessionsController);
|
||||
apiRouter.post("/fishmonger.php", fishmongerController);
|
||||
apiRouter.post("/focus.php", focusController);
|
||||
apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
|
||||
apiRouter.post("/gardening.php", gardeningController);
|
||||
apiRouter.post("/genericUpdate.php", genericUpdateController);
|
||||
apiRouter.post("/getAlliance.php", getAllianceController);
|
||||
apiRouter.post("/getFriends.php", getFriendsController);
|
||||
apiRouter.post("/getGuildDojo.php", getGuildDojoController);
|
||||
apiRouter.post("/getProfileViewingData.php", getProfileViewingDataPostController);
|
||||
apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
|
||||
apiRouter.post("/gifting.php", giftingController);
|
||||
apiRouter.post("/gildWeapon.php", gildWeaponController);
|
||||
@ -287,7 +269,6 @@ apiRouter.post("/playerSkills.php", playerSkillsController);
|
||||
apiRouter.post("/postGuildAdvertisement.php", postGuildAdvertisementController);
|
||||
apiRouter.post("/projectionManager.php", projectionManagerController);
|
||||
apiRouter.post("/purchase.php", purchaseController);
|
||||
apiRouter.post("/questControl.php", questControlController); // U17
|
||||
apiRouter.post("/redeemPromoCode.php", redeemPromoCodeController);
|
||||
apiRouter.post("/releasePet.php", releasePetController);
|
||||
apiRouter.post("/removeFromGuild.php", removeFromGuildController);
|
||||
@ -304,7 +285,6 @@ apiRouter.post("/setDojoComponentColors.php", setDojoComponentColorsController);
|
||||
apiRouter.post("/setDojoComponentMessage.php", setDojoComponentMessageController);
|
||||
apiRouter.post("/setDojoComponentSettings.php", setDojoComponentSettingsController);
|
||||
apiRouter.post("/setEquippedInstrument.php", setEquippedInstrumentController);
|
||||
apiRouter.post("/setFriendNote.php", setFriendNoteController);
|
||||
apiRouter.post("/setGuildMotd.php", setGuildMotdController);
|
||||
apiRouter.post("/setHubNpcCustomizations.php", setHubNpcCustomizationsController);
|
||||
apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController);
|
||||
@ -324,7 +304,6 @@ apiRouter.post("/trainingResult.php", trainingResultController);
|
||||
apiRouter.post("/unlockShipFeature.php", unlockShipFeatureController);
|
||||
apiRouter.post("/updateAlignment.php", updateAlignmentController);
|
||||
apiRouter.post("/updateChallengeProgress.php", updateChallengeProgressController);
|
||||
apiRouter.post("/updateInventory.php", missionInventoryUpdateController); // U26 and below
|
||||
apiRouter.post("/updateNodeIntros.php", genericUpdateController);
|
||||
apiRouter.post("/updateQuest.php", updateQuestController);
|
||||
apiRouter.post("/updateSession.php", updateSessionPostController);
|
||||
|
@ -10,7 +10,6 @@ import { getAccountInfoController } from "@/src/controllers/custom/getAccountInf
|
||||
import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
|
||||
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
|
||||
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
|
||||
import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
|
||||
|
||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
|
||||
import { createMessageController } from "@/src/controllers/custom/createMessageController";
|
||||
@ -18,11 +17,10 @@ import { addCurrencyController } from "@/src/controllers/custom/addCurrencyContr
|
||||
import { addItemsController } from "@/src/controllers/custom/addItemsController";
|
||||
import { addXpController } from "@/src/controllers/custom/addXpController";
|
||||
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 { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
|
||||
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
|
||||
|
||||
const customRouter = express.Router();
|
||||
|
||||
@ -36,7 +34,6 @@ customRouter.get("/getAccountInfo", getAccountInfoController);
|
||||
customRouter.get("/renameAccount", renameAccountController);
|
||||
customRouter.get("/ircDropped", ircDroppedController);
|
||||
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
|
||||
customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
|
||||
|
||||
customRouter.post("/createAccount", createAccountController);
|
||||
customRouter.post("/createMessage", createMessageController);
|
||||
@ -45,7 +42,6 @@ customRouter.post("/addItems", addItemsController);
|
||||
customRouter.post("/addXp", addXpController);
|
||||
customRouter.post("/import", importController);
|
||||
customRouter.post("/manageQuests", manageQuestsController);
|
||||
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
|
||||
|
||||
customRouter.get("/config", getConfigDataController);
|
||||
customRouter.post("/config", updateConfigDataController);
|
||||
|
@ -1,14 +1,14 @@
|
||||
import express from "express";
|
||||
import { aggregateSessionsController } from "@/src/controllers/dynamic/aggregateSessionsController";
|
||||
import { getGuildAdsController } from "@/src/controllers/dynamic/getGuildAdsController";
|
||||
import { getProfileViewingDataGetController } from "@/src/controllers/dynamic/getProfileViewingDataController";
|
||||
import { getProfileViewingDataController } from "@/src/controllers/dynamic/getProfileViewingDataController";
|
||||
import { worldStateController } from "@/src/controllers/dynamic/worldStateController";
|
||||
|
||||
const dynamicController = express.Router();
|
||||
|
||||
dynamicController.get("/aggregateSessions.php", aggregateSessionsController);
|
||||
dynamicController.get("/getGuildAds.php", getGuildAdsController);
|
||||
dynamicController.get("/getProfileViewingData.php", getProfileViewingDataGetController);
|
||||
dynamicController.get("/getProfileViewingData.php", getProfileViewingDataController);
|
||||
dynamicController.get("/worldState.php", worldStateController);
|
||||
|
||||
export { dynamicController };
|
||||
|
@ -24,7 +24,6 @@ interface IConfig {
|
||||
infiniteEndo?: boolean;
|
||||
infiniteRegalAya?: boolean;
|
||||
infiniteHelminthMaterials?: boolean;
|
||||
dontSubtractConsumables?: boolean;
|
||||
unlockAllShipFeatures?: boolean;
|
||||
unlockAllShipDecorations?: boolean;
|
||||
unlockAllFlavourItems?: boolean;
|
||||
@ -44,7 +43,6 @@ interface IConfig {
|
||||
noKimCooldowns?: boolean;
|
||||
instantResourceExtractorDrones?: boolean;
|
||||
noResourceExtractorDronesDamage?: boolean;
|
||||
skipClanKeyCrafting?: boolean;
|
||||
noDojoRoomBuildStage?: boolean;
|
||||
noDojoDecoBuildStage?: boolean;
|
||||
fastDojoRoomDestruction?: boolean;
|
||||
|
@ -1,47 +0,0 @@
|
||||
import { IFriendInfo } from "../types/friendTypes";
|
||||
import { getInventory } from "./inventoryService";
|
||||
import { config } from "./configService";
|
||||
import { Account } from "../models/loginModel";
|
||||
import { Types } from "mongoose";
|
||||
import { Friendship } from "../models/friendModel";
|
||||
import { toMongoDate } from "../helpers/inventoryHelpers";
|
||||
|
||||
export const addAccountDataToFriendInfo = async (info: IFriendInfo): Promise<void> => {
|
||||
const account = (await Account.findById(info._id.$oid, "DisplayName LastLogin"))!;
|
||||
info.DisplayName = account.DisplayName;
|
||||
info.LastLogin = toMongoDate(account.LastLogin);
|
||||
};
|
||||
|
||||
export const addInventoryDataToFriendInfo = async (info: IFriendInfo): Promise<void> => {
|
||||
const inventory = await getInventory(info._id.$oid, "PlayerLevel ActiveAvatarImageType");
|
||||
info.PlayerLevel = config.spoofMasteryRank == -1 ? inventory.PlayerLevel : config.spoofMasteryRank;
|
||||
info.ActiveAvatarImageType = inventory.ActiveAvatarImageType;
|
||||
};
|
||||
|
||||
export const areFriends = async (a: Types.ObjectId | string, b: Types.ObjectId | string): Promise<boolean> => {
|
||||
const [aAddedB, bAddedA] = await Promise.all([
|
||||
Friendship.exists({ owner: a, friend: b }),
|
||||
Friendship.exists({ owner: b, friend: a })
|
||||
]);
|
||||
return Boolean(aAddedB && bAddedA);
|
||||
};
|
||||
|
||||
export const areFriendsOfFriends = async (a: Types.ObjectId | string, b: Types.ObjectId | string): Promise<boolean> => {
|
||||
const [aInternalFriends, bInternalFriends] = await Promise.all([
|
||||
Friendship.find({ owner: a }),
|
||||
Friendship.find({ owner: b })
|
||||
]);
|
||||
for (const aInternalFriend of aInternalFriends) {
|
||||
if (bInternalFriends.find(x => x.friend.equals(aInternalFriend.friend))) {
|
||||
const c = aInternalFriend.friend;
|
||||
const [cAcceptedA, cAcceptedB] = await Promise.all([
|
||||
Friendship.exists({ owner: c, friend: a }),
|
||||
Friendship.exists({ owner: c, friend: b })
|
||||
]);
|
||||
if (cAcceptedA && cAcceptedB) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import { Request } from "express";
|
||||
import { getAccountIdForRequest } from "@/src/services/loginService";
|
||||
import { addLevelKeys, addRecipes, combineInventoryChanges, getInventory } from "@/src/services/inventoryService";
|
||||
import { getInventory } from "@/src/services/inventoryService";
|
||||
import { Alliance, AllianceMember, Guild, GuildAd, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel";
|
||||
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
|
||||
import {
|
||||
@ -24,6 +24,7 @@ import { Types } from "mongoose";
|
||||
import { ExportDojoRecipes, ExportResources, IDojoBuild, IDojoResearch } from "warframe-public-export-plus";
|
||||
import { logger } from "../utils/logger";
|
||||
import { config } from "./configService";
|
||||
import { Account } from "../models/loginModel";
|
||||
import { getRandomInt } from "./rngService";
|
||||
import { Inbox } from "../models/inboxModel";
|
||||
import { IFusionTreasure, ITypeCount } from "../types/inventoryTypes/inventoryTypes";
|
||||
@ -31,7 +32,6 @@ import { IInventoryChanges } from "../types/purchaseTypes";
|
||||
import { parallelForeach } from "../utils/async-utils";
|
||||
import allDecoRecipes from "@/static/fixed_responses/allDecoRecipes.json";
|
||||
import { createMessage } from "./inboxService";
|
||||
import { addAccountDataToFriendInfo, addInventoryDataToFriendInfo } from "./friendService";
|
||||
|
||||
export const getGuildForRequest = async (req: Request): Promise<TGuildDatabaseDocument> => {
|
||||
const accountId = await getAccountIdForRequest(req);
|
||||
@ -71,8 +71,12 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
||||
if (guildMember.accountId.equals(accountId)) {
|
||||
missingEntry = false;
|
||||
} else {
|
||||
dataFillInPromises.push(addAccountDataToFriendInfo(member));
|
||||
dataFillInPromises.push(addInventoryDataToFriendInfo(member));
|
||||
dataFillInPromises.push(
|
||||
(async (): Promise<void> => {
|
||||
member.DisplayName = (await Account.findById(guildMember.accountId, "DisplayName"))!.DisplayName;
|
||||
})()
|
||||
);
|
||||
dataFillInPromises.push(fillInInventoryDataForGuildMember(member));
|
||||
}
|
||||
members.push(member);
|
||||
}
|
||||
@ -101,7 +105,6 @@ export const getGuildClient = async (guild: TGuildDatabaseDocument, accountId: s
|
||||
Members: members,
|
||||
Ranks: guild.Ranks,
|
||||
Tier: guild.Tier,
|
||||
Emblem: guild.Emblem,
|
||||
Vault: getGuildVault(guild),
|
||||
ActiveDojoColorResearch: guild.ActiveDojoColorResearch,
|
||||
Class: guild.Class,
|
||||
@ -130,7 +133,7 @@ export const getGuildVault = (guild: TGuildDatabaseDocument): IGuildVault => {
|
||||
export const getDojoClient = async (
|
||||
guild: TGuildDatabaseDocument,
|
||||
status: number,
|
||||
componentId?: Types.ObjectId | string
|
||||
componentId: Types.ObjectId | string | undefined = undefined
|
||||
): Promise<IDojoClient> => {
|
||||
const dojo: IDojoClient = {
|
||||
_id: { $oid: guild._id.toString() },
|
||||
@ -171,9 +174,6 @@ export const getDojoClient = async (
|
||||
}
|
||||
if (dojoComponent.CompletionTime) {
|
||||
clientComponent.CompletionTime = toMongoDate(dojoComponent.CompletionTime);
|
||||
clientComponent.TimeRemaining = Math.trunc(
|
||||
(dojoComponent.CompletionTime.getTime() - Date.now()) / 1000
|
||||
);
|
||||
if (dojoComponent.CompletionLogPending && Date.now() >= dojoComponent.CompletionTime.getTime()) {
|
||||
const entry = guild.RoomChanges?.find(x => x.componentId.equals(dojoComponent._id));
|
||||
if (entry) {
|
||||
@ -209,9 +209,6 @@ export const getDojoClient = async (
|
||||
continue;
|
||||
}
|
||||
clientComponent.DestructionTime = toMongoDate(dojoComponent.DestructionTime);
|
||||
clientComponent.DestructionTimeRemaining = Math.trunc(
|
||||
(dojoComponent.DestructionTime.getTime() - Date.now()) / 1000
|
||||
);
|
||||
}
|
||||
} else {
|
||||
clientComponent.RegularCredits = dojoComponent.RegularCredits;
|
||||
@ -225,7 +222,6 @@ export const getDojoClient = async (
|
||||
Type: deco.Type,
|
||||
Pos: deco.Pos,
|
||||
Rot: deco.Rot,
|
||||
Scale: deco.Scale,
|
||||
Name: deco.Name,
|
||||
Sockets: deco.Sockets,
|
||||
PictureFrameInfo: deco.PictureFrameInfo
|
||||
@ -247,7 +243,6 @@ export const getDojoClient = async (
|
||||
continue;
|
||||
}
|
||||
clientDeco.CompletionTime = toMongoDate(deco.CompletionTime);
|
||||
clientDeco.TimeRemaining = Math.trunc((deco.CompletionTime.getTime() - Date.now()) / 1000);
|
||||
} else {
|
||||
clientDeco.RegularCredits = deco.RegularCredits;
|
||||
clientDeco.MiscItems = deco.MiscItems;
|
||||
@ -462,6 +457,12 @@ export const setDojoRoomLogFunded = (guild: TGuildDatabaseDocument, component: I
|
||||
}
|
||||
};
|
||||
|
||||
export const fillInInventoryDataForGuildMember = async (member: IGuildMemberClient): Promise<void> => {
|
||||
const inventory = await getInventory(member._id.$oid, "PlayerLevel ActiveAvatarImageType");
|
||||
member.PlayerLevel = config.spoofMasteryRank == -1 ? inventory.PlayerLevel : config.spoofMasteryRank;
|
||||
member.ActiveAvatarImageType = inventory.ActiveAvatarImageType;
|
||||
};
|
||||
|
||||
export const createUniqueClanName = async (name: string): Promise<string> => {
|
||||
const initialDiscriminator = getRandomInt(0, 999);
|
||||
let discriminator = initialDiscriminator;
|
||||
@ -502,7 +503,7 @@ export const hasGuildPermissionEx = (
|
||||
|
||||
export const removePigmentsFromGuildMembers = async (guildId: string | Types.ObjectId): Promise<void> => {
|
||||
const members = await GuildMember.find({ guildId, status: 0 }, "accountId");
|
||||
await parallelForeach(members, async member => {
|
||||
for (const member of members) {
|
||||
const inventory = await getInventory(member.accountId.toString(), "MiscItems");
|
||||
const index = inventory.MiscItems.findIndex(
|
||||
x => x.ItemType == "/Lotus/Types/Items/Research/DojoColors/GenericDojoColorPigment"
|
||||
@ -511,7 +512,7 @@ export const removePigmentsFromGuildMembers = async (guildId: string | Types.Obj
|
||||
inventory.MiscItems.splice(index, 1);
|
||||
await inventory.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const processGuildTechProjectContributionsUpdate = async (
|
||||
@ -551,7 +552,7 @@ export const setGuildTechLogState = (
|
||||
guild: TGuildDatabaseDocument,
|
||||
type: string,
|
||||
state: number,
|
||||
dateTime?: Date
|
||||
dateTime: Date | undefined = undefined
|
||||
): boolean => {
|
||||
guild.TechChanges ??= [];
|
||||
const entry = guild.TechChanges.find(x => x.details == type);
|
||||
@ -654,32 +655,6 @@ export const checkClanAscensionHasRequiredContributors = async (guild: TGuildDat
|
||||
}
|
||||
};
|
||||
|
||||
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 => {
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
|
||||
|
@ -2,7 +2,6 @@ import { Types } from "mongoose";
|
||||
import {
|
||||
IEquipmentClient,
|
||||
IEquipmentDatabase,
|
||||
IItemConfig,
|
||||
IOperatorConfigClient,
|
||||
IOperatorConfigDatabase
|
||||
} from "../types/inventoryTypes/commonInventoryTypes";
|
||||
@ -38,7 +37,6 @@ import {
|
||||
} from "../types/inventoryTypes/inventoryTypes";
|
||||
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
|
||||
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "../types/saveLoadoutTypes";
|
||||
import { slotNames } from "../types/purchaseTypes";
|
||||
|
||||
const convertDate = (value: IMongoDate): Date => {
|
||||
return new Date(parseInt(value.$date.$numberLong));
|
||||
@ -170,25 +168,10 @@ const convertPendingRecipe = (client: IPendingRecipeClient): IPendingRecipeDatab
|
||||
const convertNemesis = (client: INemesisClient): INemesisDatabase => {
|
||||
return {
|
||||
...client,
|
||||
fp: BigInt(client.fp),
|
||||
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 => {
|
||||
for (const key of equipmentKeys) {
|
||||
if (client[key] !== undefined) {
|
||||
@ -229,7 +212,20 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
replaceArray<IOperatorConfigDatabase>(db[key], client[key].map(convertOperatorConfig));
|
||||
}
|
||||
}
|
||||
for (const key of slotNames) {
|
||||
for (const key of [
|
||||
"SuitBin",
|
||||
"WeaponBin",
|
||||
"SentinelBin",
|
||||
"SpaceSuitBin",
|
||||
"SpaceWeaponBin",
|
||||
"PvpBonusLoadoutBin",
|
||||
"PveBonusLoadoutBin",
|
||||
"RandomModBin",
|
||||
"MechBin",
|
||||
"CrewMemberBin",
|
||||
"OperatorAmpBin",
|
||||
"CrewShipSalvageBin"
|
||||
] as const) {
|
||||
if (client[key] !== undefined) {
|
||||
replaceSlots(db[key], client[key]);
|
||||
}
|
||||
@ -367,7 +363,7 @@ export const importInventory = (db: TInventoryDatabaseDocument, client: Partial<
|
||||
db.PlayerSkills = client.PlayerSkills;
|
||||
}
|
||||
if (client.LotusCustomization !== undefined) {
|
||||
db.LotusCustomization = convertItemConfig(client.LotusCustomization);
|
||||
db.LotusCustomization = client.LotusCustomization;
|
||||
}
|
||||
if (client.CollectibleSeries !== undefined) {
|
||||
db.CollectibleSeries = client.CollectibleSeries;
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
IKubrowPetEggDatabase,
|
||||
IKubrowPetEggClient,
|
||||
ILibraryDailyTaskInfo,
|
||||
ICalendarProgress,
|
||||
IDroneClient,
|
||||
IUpgradeClient,
|
||||
TPartialStartingGear,
|
||||
@ -25,10 +26,7 @@ import {
|
||||
ICrewMemberClient,
|
||||
Status,
|
||||
IKubrowPetDetailsDatabase,
|
||||
ITraits,
|
||||
ICalendarProgress,
|
||||
INemesisWeaponTargetFingerprint,
|
||||
INemesisPetTargetFingerprint
|
||||
ITraits
|
||||
} from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
|
||||
import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes";
|
||||
@ -69,7 +67,6 @@ import {
|
||||
import { createShip } from "./shipService";
|
||||
import {
|
||||
catbrowDetails,
|
||||
fromMongoDate,
|
||||
kubrowDetails,
|
||||
kubrowFurPatternsWeights,
|
||||
kubrowWeights,
|
||||
@ -81,8 +78,6 @@ import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
||||
import { getRandomElement, getRandomInt, getRandomWeightedReward, SRng } from "./rngService";
|
||||
import { createMessage } from "./inboxService";
|
||||
import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper";
|
||||
import { getWorldState } from "./worldStateService";
|
||||
import { getInnateDamageTag, getInnateDamageValue } from "../helpers/nemesisHelpers";
|
||||
|
||||
export const createInventory = async (
|
||||
accountOwnerId: Types.ObjectId,
|
||||
@ -96,6 +91,7 @@ export const createInventory = async (
|
||||
});
|
||||
|
||||
inventory.LibraryAvailableDailyTaskInfo = createLibraryDailyTask();
|
||||
inventory.CalendarProgress = createCalendar();
|
||||
inventory.RewardSeed = generateRewardSeed();
|
||||
inventory.DuviriInfo = {
|
||||
Seed: generateRewardSeed(),
|
||||
@ -124,15 +120,10 @@ export const createInventory = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const generateRewardSeed = (): bigint => {
|
||||
const hiDword = getRandomInt(0, 0x7fffffff);
|
||||
const loDword = getRandomInt(0, 0xffffffff);
|
||||
let seed = (BigInt(hiDword) << 32n) | BigInt(loDword);
|
||||
if (Math.random() < 0.5) {
|
||||
seed *= -1n;
|
||||
seed -= 1n;
|
||||
}
|
||||
return seed;
|
||||
export const generateRewardSeed = (): number => {
|
||||
const min = -Number.MAX_SAFE_INTEGER;
|
||||
const max = Number.MAX_SAFE_INTEGER;
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
};
|
||||
|
||||
//TODO: RawUpgrades might need to return a LastAdded
|
||||
@ -147,13 +138,8 @@ const awakeningRewards = [
|
||||
|
||||
export const addStartingGear = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
startingGear?: TPartialStartingGear
|
||||
startingGear: TPartialStartingGear | undefined = undefined
|
||||
): Promise<IInventoryChanges> => {
|
||||
if (inventory.ReceivedStartingGear) {
|
||||
throw new Error(`account has already received starting gear`);
|
||||
}
|
||||
inventory.ReceivedStartingGear = true;
|
||||
|
||||
const { LongGuns, Pistols, Suits, Melee } = startingGear || {
|
||||
LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }],
|
||||
Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }],
|
||||
@ -163,22 +149,23 @@ export const addStartingGear = async (
|
||||
|
||||
//TODO: properly merge weapon bin changes it is currently static here
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(inventory, "Pistols", Pistols[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(inventory, "Melee", Melee[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
await addPowerSuit(inventory, Suits[0].ItemType, { IsNew: false }, inventoryChanges);
|
||||
addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges);
|
||||
addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges);
|
||||
addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges);
|
||||
await addPowerSuit(inventory, Suits[0].ItemType, inventoryChanges);
|
||||
addEquipment(
|
||||
inventory,
|
||||
"DataKnives",
|
||||
"/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon",
|
||||
{ XP: 450_000, IsNew: false },
|
||||
inventoryChanges
|
||||
undefined,
|
||||
inventoryChanges,
|
||||
{ XP: 450_000 }
|
||||
);
|
||||
addEquipment(
|
||||
inventory,
|
||||
"Scoops",
|
||||
"/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest",
|
||||
{ IsNew: false },
|
||||
undefined,
|
||||
inventoryChanges
|
||||
);
|
||||
|
||||
@ -202,6 +189,11 @@ export const addStartingGear = async (
|
||||
combineInventoryChanges(inventoryChanges, inventoryDelta);
|
||||
}
|
||||
|
||||
if (inventory.ReceivedStartingGear) {
|
||||
logger.warn(`account already had starting gear but asked for it again?!`);
|
||||
}
|
||||
inventory.ReceivedStartingGear = true;
|
||||
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
@ -216,15 +208,6 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
|
||||
for (const key in delta) {
|
||||
if (!(key in InventoryChanges)) {
|
||||
InventoryChanges[key] = delta[key];
|
||||
} else if (key == "MiscItems") {
|
||||
for (const deltaItem of delta[key]!) {
|
||||
const existing = InventoryChanges[key]!.find(x => x.ItemType == deltaItem.ItemType);
|
||||
if (existing) {
|
||||
existing.ItemCount += deltaItem.ItemCount;
|
||||
} else {
|
||||
InventoryChanges[key]!.push(deltaItem);
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(delta[key])) {
|
||||
const left = InventoryChanges[key] as object[];
|
||||
const right: object[] = delta[key];
|
||||
@ -257,7 +240,7 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del
|
||||
|
||||
export const getInventory = async (
|
||||
accountOwnerId: string,
|
||||
projection?: string
|
||||
projection: string | undefined = undefined
|
||||
): Promise<TInventoryDatabaseDocument> => {
|
||||
const inventory = await Inventory.findOne({ accountOwnerId: accountOwnerId }, projection);
|
||||
|
||||
@ -331,9 +314,7 @@ export const addItem = async (
|
||||
typeName: string,
|
||||
quantity: number = 1,
|
||||
premiumPurchase: boolean = false,
|
||||
seed?: bigint,
|
||||
targetFingerprint?: string,
|
||||
exactQuantity: boolean = false
|
||||
seed?: bigint
|
||||
): Promise<IInventoryChanges> => {
|
||||
// Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments.
|
||||
if (typeName in ExportBundles) {
|
||||
@ -488,12 +469,6 @@ export const addItem = async (
|
||||
};
|
||||
}
|
||||
if (typeName in ExportGear) {
|
||||
// Multipling by purchase quantity for gear because:
|
||||
// - The Saya's Vigil scanner message has it as a non-counted attachment.
|
||||
// - Blueprints for Ancient Protector Specter, Shield Osprey Specter, etc. have num=1 despite giving their purchaseQuantity.
|
||||
if (!exactQuantity) {
|
||||
quantity *= ExportGear[typeName].purchaseQuantity ?? 1;
|
||||
}
|
||||
const consumablesChanges = [
|
||||
{
|
||||
ItemType: typeName,
|
||||
@ -542,13 +517,14 @@ export const addItem = async (
|
||||
]
|
||||
});
|
||||
}
|
||||
if (targetFingerprint) {
|
||||
const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisWeaponTargetFingerprint;
|
||||
defaultOverwrites.UpgradeType = targetFingerprintObj.ItemType;
|
||||
defaultOverwrites.UpgradeFingerprint = JSON.stringify(targetFingerprintObj.UpgradeFingerprint);
|
||||
defaultOverwrites.ItemName = targetFingerprintObj.Name;
|
||||
}
|
||||
const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName, defaultOverwrites);
|
||||
const inventoryChanges = addEquipment(
|
||||
inventory,
|
||||
weapon.productCategory,
|
||||
typeName,
|
||||
[],
|
||||
{},
|
||||
defaultOverwrites
|
||||
);
|
||||
if (weapon.additionalItems) {
|
||||
for (const item of weapon.additionalItems) {
|
||||
combineInventoryChanges(inventoryChanges, await addItem(inventory, item, 1));
|
||||
@ -556,32 +532,7 @@ export const addItem = async (
|
||||
}
|
||||
return {
|
||||
...inventoryChanges,
|
||||
...occupySlot(
|
||||
inventory,
|
||||
productCategoryToInventoryBin(weapon.productCategory) ?? InventorySlot.WEAPONS,
|
||||
premiumPurchase
|
||||
)
|
||||
};
|
||||
} else if (targetFingerprint) {
|
||||
// Sister's Hound
|
||||
const targetFingerprintObj = JSON.parse(targetFingerprint) as INemesisPetTargetFingerprint;
|
||||
const head = targetFingerprintObj.Parts[0];
|
||||
const defaultOverwrites: Partial<IEquipmentDatabase> = {
|
||||
ModularParts: targetFingerprintObj.Parts,
|
||||
ItemName: targetFingerprintObj.Name,
|
||||
Configs: applyDefaultUpgrades(inventory, ExportWeapons[head].defaultUpgrades)
|
||||
};
|
||||
const 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"
|
||||
}[head] as string;
|
||||
return {
|
||||
...addEquipment(inventory, "MoaPets", itemType, defaultOverwrites),
|
||||
...occupySlot(inventory, InventorySlot.SENTINELS, premiumPurchase)
|
||||
...occupySlot(inventory, InventorySlot.WEAPONS, premiumPurchase)
|
||||
};
|
||||
} else {
|
||||
// Modular weapon parts
|
||||
@ -629,7 +580,7 @@ export const addItem = async (
|
||||
}
|
||||
if (typeName in ExportFusionBundles) {
|
||||
const fusionPointsTotal = ExportFusionBundles[typeName].fusionPoints * quantity;
|
||||
addFusionPoints(inventory, fusionPointsTotal);
|
||||
inventory.FusionPoints += fusionPointsTotal;
|
||||
return {
|
||||
FusionPoints: fusionPointsTotal
|
||||
};
|
||||
@ -670,9 +621,12 @@ export const addItem = async (
|
||||
switch (typeName.substr(1).split("/")[2]) {
|
||||
default: {
|
||||
return {
|
||||
...(await addPowerSuit(inventory, typeName, {
|
||||
Features: premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
|
||||
})),
|
||||
...(await addPowerSuit(
|
||||
inventory,
|
||||
typeName,
|
||||
{},
|
||||
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
|
||||
)),
|
||||
...occupySlot(inventory, InventorySlot.SUITS, premiumPurchase)
|
||||
};
|
||||
}
|
||||
@ -789,10 +743,6 @@ export const addItem = async (
|
||||
break;
|
||||
}
|
||||
case "NeutralCreatures": {
|
||||
if (inventory.Horses.length != 0) {
|
||||
logger.warn("refusing to add Horse because account already has one");
|
||||
return {};
|
||||
}
|
||||
const horseIndex = inventory.Horses.push({ ItemType: typeName });
|
||||
return {
|
||||
Horses: [inventory.Horses[horseIndex - 1].toJSON<IEquipmentClient>()]
|
||||
@ -871,8 +821,7 @@ const addSentinel = (
|
||||
|
||||
const features = premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined;
|
||||
const sentinelIndex =
|
||||
inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0, Features: features, IsNew: true }) -
|
||||
1;
|
||||
inventory.Sentinels.push({ ItemType: sentinelName, Configs: configs, XP: 0, Features: features }) - 1;
|
||||
inventoryChanges.Sentinels ??= [];
|
||||
inventoryChanges.Sentinels.push(inventory.Sentinels[sentinelIndex].toJSON<IEquipmentClient>());
|
||||
|
||||
@ -896,8 +845,8 @@ const addSentinelWeapon = (
|
||||
export const addPowerSuit = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
powersuitName: string,
|
||||
defaultOverwrites?: Partial<IEquipmentDatabase>,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
features: number | undefined = undefined
|
||||
): Promise<IInventoryChanges> => {
|
||||
const powersuit = ExportWarframes[powersuitName] as IPowersuit | undefined;
|
||||
const exalted = powersuit?.exalted ?? [];
|
||||
@ -911,20 +860,15 @@ export const addPowerSuit = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
const suit: Omit<IEquipmentDatabase, "_id"> = Object.assign(
|
||||
{
|
||||
const suitIndex =
|
||||
inventory.Suits.push({
|
||||
ItemType: powersuitName,
|
||||
Configs: [],
|
||||
UpgradeVer: 101,
|
||||
XP: 0,
|
||||
Features: features,
|
||||
IsNew: true
|
||||
},
|
||||
defaultOverwrites
|
||||
);
|
||||
if (!suit.IsNew) {
|
||||
suit.IsNew = undefined;
|
||||
}
|
||||
const suitIndex = inventory.Suits.push(suit) - 1;
|
||||
}) - 1;
|
||||
inventoryChanges.Suits ??= [];
|
||||
inventoryChanges.Suits.push(inventory.Suits[suitIndex].toJSON<IEquipmentClient>());
|
||||
return inventoryChanges;
|
||||
@ -934,7 +878,7 @@ export const addMechSuit = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
mechsuitName: string,
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
features?: number
|
||||
features: number | undefined = undefined
|
||||
): Promise<IInventoryChanges> => {
|
||||
const powersuit = ExportWarframes[mechsuitName] as IPowersuit | undefined;
|
||||
const exalted = powersuit?.exalted ?? [];
|
||||
@ -986,7 +930,7 @@ export const addSpaceSuit = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
spacesuitName: string,
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
features?: number
|
||||
features: number | undefined = undefined
|
||||
): IInventoryChanges => {
|
||||
const suitIndex =
|
||||
inventory.SpaceSuits.push({
|
||||
@ -1125,15 +1069,6 @@ export const updateCurrency = (
|
||||
return currencyChanges;
|
||||
};
|
||||
|
||||
export const addFusionPoints = (inventory: TInventoryDatabaseDocument, add: number): number => {
|
||||
if (inventory.FusionPoints + add > 2147483647) {
|
||||
logger.warn(`capping FusionPoints balance at 2147483647`);
|
||||
add = 2147483647 - inventory.FusionPoints;
|
||||
}
|
||||
inventory.FusionPoints += add;
|
||||
return add;
|
||||
};
|
||||
|
||||
const standingLimitBinToInventoryKey: Record<
|
||||
Exclude<TStandingLimitBin, "STANDING_LIMIT_BIN_NONE">,
|
||||
keyof IDailyAffiliations
|
||||
@ -1245,21 +1180,20 @@ export const addEquipment = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
category: TEquipmentKey,
|
||||
type: string,
|
||||
defaultOverwrites?: Partial<IEquipmentDatabase>,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
modularParts: string[] | undefined = undefined,
|
||||
inventoryChanges: IInventoryChanges = {},
|
||||
defaultOverwrites: Partial<IEquipmentDatabase> | undefined = undefined
|
||||
): IInventoryChanges => {
|
||||
const equipment: Omit<IEquipmentDatabase, "_id"> = Object.assign(
|
||||
const equipment = Object.assign(
|
||||
{
|
||||
ItemType: type,
|
||||
Configs: [],
|
||||
XP: 0,
|
||||
IsNew: category != "CrewShipWeapons" && category != "CrewShipSalvagedWeapons"
|
||||
ModularParts: modularParts,
|
||||
IsNew: category != "CrewShipWeapons" && category != "CrewShipSalvagedWeapons" ? true : undefined
|
||||
},
|
||||
defaultOverwrites
|
||||
);
|
||||
if (!equipment.IsNew) {
|
||||
equipment.IsNew = undefined;
|
||||
}
|
||||
const index = inventory[category].push(equipment) - 1;
|
||||
|
||||
inventoryChanges[category] ??= [];
|
||||
@ -1288,16 +1222,12 @@ export const addSkin = (
|
||||
typeName: string,
|
||||
inventoryChanges: IInventoryChanges = {}
|
||||
): IInventoryChanges => {
|
||||
if (inventory.WeaponSkins.find(x => x.ItemType == typeName)) {
|
||||
logger.debug(`refusing to add WeaponSkin ${typeName} because account already owns it`);
|
||||
} else {
|
||||
const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
inventoryChanges.WeaponSkins ??= [];
|
||||
(inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push(
|
||||
inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>()
|
||||
);
|
||||
}
|
||||
const index = inventory.WeaponSkins.push({ ItemType: typeName, IsNew: true }) - 1;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
inventoryChanges.WeaponSkins ??= [];
|
||||
(inventoryChanges.WeaponSkins as IWeaponSkinClient[]).push(
|
||||
inventory.WeaponSkins[index].toJSON<IWeaponSkinClient>()
|
||||
);
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
@ -1483,20 +1413,21 @@ export const addEmailItem = async (
|
||||
return inventoryChanges;
|
||||
};
|
||||
|
||||
export const applyClientEquipmentUpdates = (
|
||||
//TODO: wrong id is not erroring
|
||||
export const addGearExpByCategory = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
gearArray: IEquipmentClient[],
|
||||
categoryName: TEquipmentKey
|
||||
): void => {
|
||||
const category = inventory[categoryName];
|
||||
|
||||
gearArray.forEach(({ ItemId, XP, InfestationDate }) => {
|
||||
const item = category.id(ItemId.$oid);
|
||||
if (!item) {
|
||||
throw new Error(`No item with id ${ItemId.$oid} in ${categoryName}`);
|
||||
gearArray.forEach(({ ItemId, XP }) => {
|
||||
if (!XP) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (XP) {
|
||||
const item = category.id(ItemId.$oid);
|
||||
if (item) {
|
||||
item.XP ??= 0;
|
||||
item.XP += XP;
|
||||
|
||||
@ -1511,31 +1442,9 @@ export const applyClientEquipmentUpdates = (
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (InfestationDate) {
|
||||
// 2147483647000 means cured, otherwise became infected
|
||||
item.InfestationDate =
|
||||
InfestationDate.$date.$numberLong == "2147483647000" ? new Date(0) : fromMongoDate(InfestationDate);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const addMiscItem = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
type: string,
|
||||
count: number,
|
||||
inventoryChanges: IInventoryChanges
|
||||
): void => {
|
||||
const miscItemChanges: IMiscItem[] = [
|
||||
{
|
||||
ItemType: type,
|
||||
ItemCount: count
|
||||
}
|
||||
];
|
||||
addMiscItems(inventory, miscItemChanges);
|
||||
combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });
|
||||
};
|
||||
|
||||
export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray: IMiscItem[]): void => {
|
||||
const { MiscItems } = inventory;
|
||||
|
||||
@ -1815,7 +1724,7 @@ export const addKeyChainItems = async (
|
||||
};
|
||||
|
||||
export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => {
|
||||
const enemyTypes = getRandomElement(libraryDailyTasks)!;
|
||||
const enemyTypes = getRandomElement(libraryDailyTasks);
|
||||
const enemyAvatar = ExportEnemies.avatars[enemyTypes[0]];
|
||||
const scansRequired = getRandomInt(2, 4);
|
||||
return {
|
||||
@ -1829,6 +1738,20 @@ export const createLibraryDailyTask = (): ILibraryDailyTaskInfo => {
|
||||
};
|
||||
};
|
||||
|
||||
const createCalendar = (): ICalendarProgress => {
|
||||
return {
|
||||
Version: 19,
|
||||
Iteration: 2,
|
||||
YearProgress: { Upgrades: [] },
|
||||
SeasonProgress: {
|
||||
SeasonType: "CST_SPRING",
|
||||
LastCompletedDayIdx: -1,
|
||||
LastCompletedChallengeDayIdx: -1,
|
||||
ActivatedChallenges: []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const setupKahlSyndicate = (inventory: TInventoryDatabaseDocument): void => {
|
||||
inventory.Affiliations.push({
|
||||
Title: 1,
|
||||
@ -1864,132 +1787,4 @@ export const cleanupInventory = (inventory: TInventoryDatabaseDocument): void =>
|
||||
logger.debug(`removing FreeFavorsEarned from LibrarySyndicate`);
|
||||
LibrarySyndicate.FreeFavorsEarned = undefined;
|
||||
}
|
||||
|
||||
if (inventory.LotusCustomization) {
|
||||
if (
|
||||
Array.isArray(inventory.LotusCustomization.attcol) ||
|
||||
Array.isArray(inventory.LotusCustomization.sigcol) ||
|
||||
Array.isArray(inventory.LotusCustomization.eyecol) ||
|
||||
Array.isArray(inventory.LotusCustomization.facial) ||
|
||||
Array.isArray(inventory.LotusCustomization.cloth) ||
|
||||
Array.isArray(inventory.LotusCustomization.syancol)
|
||||
) {
|
||||
logger.debug(`fixing empty objects represented as empty arrays in LotusCustomization`);
|
||||
inventory.LotusCustomization.attcol = {};
|
||||
inventory.LotusCustomization.sigcol = {};
|
||||
inventory.LotusCustomization.eyecol = {};
|
||||
inventory.LotusCustomization.facial = {};
|
||||
inventory.LotusCustomization.cloth = {};
|
||||
inventory.LotusCustomization.syancol = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICalendarProgress => {
|
||||
const currentSeason = getWorldState().KnownCalendarSeasons[0];
|
||||
|
||||
if (!inventory.CalendarProgress) {
|
||||
inventory.CalendarProgress = {
|
||||
Version: 19,
|
||||
Iteration: currentSeason.YearIteration,
|
||||
YearProgress: {
|
||||
Upgrades: []
|
||||
},
|
||||
SeasonProgress: {
|
||||
SeasonType: currentSeason.Season,
|
||||
LastCompletedDayIdx: 0,
|
||||
LastCompletedChallengeDayIdx: 0,
|
||||
ActivatedChallenges: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const yearRolledOver = inventory.CalendarProgress.Iteration != currentSeason.YearIteration;
|
||||
if (yearRolledOver) {
|
||||
inventory.CalendarProgress.Iteration = currentSeason.YearIteration;
|
||||
inventory.CalendarProgress.YearProgress.Upgrades = [];
|
||||
}
|
||||
if (yearRolledOver || inventory.CalendarProgress.SeasonProgress.SeasonType != currentSeason.Season) {
|
||||
inventory.CalendarProgress.SeasonProgress.SeasonType = currentSeason.Season;
|
||||
inventory.CalendarProgress.SeasonProgress.LastCompletedDayIdx = -1;
|
||||
inventory.CalendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = -1;
|
||||
inventory.CalendarProgress.SeasonProgress.ActivatedChallenges = [];
|
||||
}
|
||||
|
||||
return inventory.CalendarProgress;
|
||||
};
|
||||
|
||||
export const giveNemesisWeaponRecipe = (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
weaponType: string,
|
||||
nemesisName: string = "AGOR ROK",
|
||||
weaponLoc?: string,
|
||||
KillingSuit: string = "/Lotus/Powersuits/Ember/Ember",
|
||||
fp: bigint = generateRewardSeed()
|
||||
): void => {
|
||||
if (!weaponLoc) {
|
||||
weaponLoc = ExportWeapons[weaponType].name;
|
||||
}
|
||||
const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == weaponType)![0];
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: recipeType,
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
inventory.PendingRecipes.push({
|
||||
CompletionDate: new Date(),
|
||||
ItemType: recipeType,
|
||||
TargetFingerprint: JSON.stringify({
|
||||
ItemType: "/Lotus/Weapons/Grineer/KuvaLich/Upgrades/InnateDamageRandomMod",
|
||||
UpgradeFingerprint: {
|
||||
compat: weaponType,
|
||||
buffs: [
|
||||
{
|
||||
Tag: getInnateDamageTag(KillingSuit),
|
||||
Value: getInnateDamageValue(fp)
|
||||
}
|
||||
]
|
||||
},
|
||||
Name: weaponLoc + "|" + nemesisName
|
||||
} satisfies INemesisWeaponTargetFingerprint)
|
||||
});
|
||||
};
|
||||
|
||||
export const giveNemesisPetRecipe = (inventory: TInventoryDatabaseDocument, nemesisName: string = "AGOR ROK"): void => {
|
||||
const head = getRandomElement([
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadA",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadB",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartHeadC"
|
||||
])!;
|
||||
const body = getRandomElement([
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyA",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyB",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartBodyC"
|
||||
])!;
|
||||
const legs = getRandomElement([
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsA",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsB",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartLegsC"
|
||||
])!;
|
||||
const tail = getRandomElement([
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailA",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailB",
|
||||
"/Lotus/Types/Friendly/Pets/ZanukaPets/ZanukaPetParts/ZanukaPetPartTailC"
|
||||
])!;
|
||||
const recipeType = Object.entries(ExportRecipes).find(arr => arr[1].resultType == head)![0];
|
||||
addRecipes(inventory, [
|
||||
{
|
||||
ItemType: recipeType,
|
||||
ItemCount: 1
|
||||
}
|
||||
]);
|
||||
inventory.PendingRecipes.push({
|
||||
CompletionDate: new Date(),
|
||||
ItemType: recipeType,
|
||||
TargetFingerprint: JSON.stringify({
|
||||
Parts: [head, body, legs, tail],
|
||||
Name: "/Lotus/Language/Pets/ZanukaPetName|" + nemesisName
|
||||
} satisfies INemesisPetTargetFingerprint)
|
||||
});
|
||||
};
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
dict_uk,
|
||||
dict_zh,
|
||||
ExportArcanes,
|
||||
ExportBoosters,
|
||||
ExportCustoms,
|
||||
ExportDrones,
|
||||
ExportGear,
|
||||
@ -218,30 +217,15 @@ export const convertInboxMessage = (message: IInboxMessage): IMessage => {
|
||||
};
|
||||
|
||||
export const isStoreItem = (type: string): boolean => {
|
||||
return type.startsWith("/Lotus/StoreItems/") || type in ExportBoosters;
|
||||
return type.startsWith("/Lotus/StoreItems/");
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
export const fromStoreItem = (type: string): string => {
|
||||
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`);
|
||||
return "/Lotus/" + type.substring("/Lotus/StoreItems/".length);
|
||||
};
|
||||
|
||||
export const getDefaultUpgrades = (parts: string[]): IDefaultUpgrade[] | undefined => {
|
||||
|
@ -77,6 +77,7 @@ const getRandomLoginReward = (rng: CRng, day: number, inventory: TInventoryDatab
|
||||
const reward = rng.randomReward(randomRewards)!;
|
||||
//const reward = randomRewards.find(x => x.RewardType == "RT_BOOSTER")!;
|
||||
if (reward.RewardType == "RT_RANDOM_RECIPE") {
|
||||
// Not very faithful implementation but roughly the same idea
|
||||
const masteredItems = new Set();
|
||||
for (const entry of inventory.XPInfo) {
|
||||
masteredItems.add(entry.ItemType);
|
||||
@ -94,15 +95,15 @@ const getRandomLoginReward = (rng: CRng, day: number, inventory: TInventoryDatab
|
||||
}
|
||||
const eligibleRecipes: string[] = [];
|
||||
for (const [uniqueName, recipe] of Object.entries(ExportRecipes)) {
|
||||
if (!recipe.excludeFromMarket && unmasteredItems.has(recipe.resultType)) {
|
||||
if (unmasteredItems.has(recipe.resultType)) {
|
||||
eligibleRecipes.push(uniqueName);
|
||||
}
|
||||
}
|
||||
if (eligibleRecipes.length == 0) {
|
||||
// This account has all applicable warframes and weapons already mastered (filthy cheater), need a different reward.
|
||||
// This account has all warframes and weapons already mastered (filthy cheater), need a different reward.
|
||||
return getRandomLoginReward(rng, day, inventory);
|
||||
}
|
||||
reward.StoreItemType = toStoreItem(rng.randomElement(eligibleRecipes)!);
|
||||
reward.StoreItemType = toStoreItem(rng.randomElement(eligibleRecipes));
|
||||
}
|
||||
return {
|
||||
//_id: toOid(new Types.ObjectId()),
|
||||
|
@ -19,8 +19,8 @@ import {
|
||||
addCrewShipRawSalvage,
|
||||
addEmailItem,
|
||||
addFocusXpIncreases,
|
||||
addFusionPoints,
|
||||
addFusionTreasures,
|
||||
addGearExpByCategory,
|
||||
addItem,
|
||||
addLevelKeys,
|
||||
addLoreFragmentScans,
|
||||
@ -29,14 +29,9 @@ import {
|
||||
addMods,
|
||||
addRecipes,
|
||||
addShipDecorations,
|
||||
addSkin,
|
||||
addStanding,
|
||||
applyClientEquipmentUpdates,
|
||||
combineInventoryChanges,
|
||||
generateRewardSeed,
|
||||
getCalendarProgress,
|
||||
giveNemesisPetRecipe,
|
||||
giveNemesisWeaponRecipe,
|
||||
updateCurrency,
|
||||
updateSyndicate
|
||||
} from "@/src/services/inventoryService";
|
||||
@ -48,20 +43,19 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento
|
||||
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
|
||||
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { handleStoreItemAcquisition } from "./purchaseService";
|
||||
import { IMissionCredits, IMissionReward } from "../types/missionTypes";
|
||||
import { IMissionReward } from "../types/missionTypes";
|
||||
import { crackRelic } from "@/src/helpers/relicHelper";
|
||||
import { createMessage } from "./inboxService";
|
||||
import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json";
|
||||
import kuriaMessage75 from "@/static/fixed_responses/kuriaMessages/seventyFivePercent.json";
|
||||
import kuriaMessage100 from "@/static/fixed_responses/kuriaMessages/oneHundredPercent.json";
|
||||
import conservationAnimals from "@/static/fixed_responses/conservationAnimals.json";
|
||||
import { getInfNodes, getWeaponsForManifest, sendCodaFinishedMessage } from "@/src/helpers/nemesisHelpers";
|
||||
import { getInfNodes } from "@/src/helpers/nemesisHelpers";
|
||||
import { Loadout } from "../models/inventoryModels/loadoutModel";
|
||||
import { ILoadoutConfigDatabase } from "../types/saveLoadoutTypes";
|
||||
import { getLiteSortie, getSortie, idToBountyCycle, idToDay, idToWeek, pushClassicBounties } from "./worldStateService";
|
||||
import { getLiteSortie, getWorldState, idToWeek } from "./worldStateService";
|
||||
import { config } from "./configService";
|
||||
import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json";
|
||||
import { ISyndicateMissionInfo } from "../types/worldStateTypes";
|
||||
|
||||
const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => {
|
||||
// For Spy missions, e.g. 3 vaults cracked = A, B, C
|
||||
@ -73,23 +67,19 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
|
||||
return rotations;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const missionIndex: number | undefined = ExportRegions[rewardInfo.node]?.missionIndex;
|
||||
|
||||
// For Rescue missions
|
||||
if (missionIndex == 3 && rewardInfo.rewardTier) {
|
||||
if (rewardInfo.node in ExportRegions && ExportRegions[rewardInfo.node].missionIndex == 3 && rewardInfo.rewardTier) {
|
||||
return [rewardInfo.rewardTier];
|
||||
}
|
||||
|
||||
const rotationCount = rewardInfo.rewardQualifications?.length || 0;
|
||||
|
||||
// Empty or absent rewardQualifications should not give rewards when:
|
||||
// - Completing only 1 zone of (E)SO (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1823)
|
||||
// - Aborting a railjack mission (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
|
||||
if (rotationCount == 0 && missionIndex != 30 && missionIndex != 32) {
|
||||
return [0];
|
||||
// Aborting a railjack mission should not give any rewards (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1741)
|
||||
if (rewardInfo.rewardQualifications === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const rotationCount = rewardInfo.rewardQualifications.length || 0;
|
||||
if (rotationCount === 0) return [0];
|
||||
|
||||
const rotationPattern =
|
||||
tierOverride === undefined
|
||||
? [0, 0, 1, 2] // A, A, B, C
|
||||
@ -144,6 +134,38 @@ export const addMissionInventoryUpdates = async (
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Somewhat heuristically detect G3 capture:
|
||||
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1365
|
||||
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1694
|
||||
// - https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1724
|
||||
if (
|
||||
inventoryUpdates.MissionFailed &&
|
||||
inventoryUpdates.MissionStatus == "GS_FAILURE" &&
|
||||
inventoryUpdates.ObjectiveReached &&
|
||||
!inventoryUpdates.LockedWeaponGroup &&
|
||||
!inventory.LockedWeaponGroup &&
|
||||
!inventoryUpdates.LevelKeyName
|
||||
) {
|
||||
const loadout = (await Loadout.findById(inventory.LoadOutPresets, "NORMAL"))!;
|
||||
const config = loadout.NORMAL.id(inventory.CurrentLoadOutIds[0].$oid)!;
|
||||
const SuitId = new Types.ObjectId(config.s!.ItemId.$oid);
|
||||
|
||||
inventory.BrandedSuits ??= [];
|
||||
if (!inventory.BrandedSuits.find(x => x.equals(SuitId))) {
|
||||
inventory.BrandedSuits.push(SuitId);
|
||||
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
|
||||
msg: "/Lotus/Language/G1Quests/BrandedMessage",
|
||||
sub: "/Lotus/Language/G1Quests/BrandedTitle",
|
||||
att: ["/Lotus/Types/Recipes/Components/BrandRemovalBlueprint"],
|
||||
highPriority: true // TOVERIFY: I cannot find any content of this within the last 10 years so I can only assume that highPriority is set (it certainly would make sense), but I just don't know for sure that it is so on live.
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo) {
|
||||
if (inventoryUpdates.RewardInfo.periodicMissionTag) {
|
||||
@ -162,12 +184,6 @@ export const addMissionInventoryUpdates = async (
|
||||
if (inventoryUpdates.RewardInfo.NemesisAbandonedRewards) {
|
||||
inventory.NemesisAbandonedRewards = inventoryUpdates.RewardInfo.NemesisAbandonedRewards;
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo.NemesisHenchmenKills && inventory.Nemesis) {
|
||||
inventory.Nemesis.HenchmenKilled += inventoryUpdates.RewardInfo.NemesisHenchmenKills;
|
||||
}
|
||||
if (inventoryUpdates.RewardInfo.NemesisHintProgress && inventory.Nemesis) {
|
||||
inventory.Nemesis.HintProgress += inventoryUpdates.RewardInfo.NemesisHintProgress;
|
||||
}
|
||||
if (inventoryUpdates.MissionStatus == "GS_SUCCESS" && inventoryUpdates.RewardInfo.jobId) {
|
||||
// e.g. for Profit-Taker Phase 1:
|
||||
// JobTier: -6,
|
||||
@ -249,14 +265,7 @@ export const addMissionInventoryUpdates = async (
|
||||
addMiscItems(inventory, value);
|
||||
break;
|
||||
case "Consumables":
|
||||
if (config.dontSubtractConsumables) {
|
||||
addConsumables(
|
||||
inventory,
|
||||
value.filter(x => x.ItemCount > 0)
|
||||
);
|
||||
} else {
|
||||
addConsumables(inventory, value);
|
||||
}
|
||||
addConsumables(inventory, value);
|
||||
break;
|
||||
case "Recipes":
|
||||
addRecipes(inventory, value);
|
||||
@ -278,14 +287,14 @@ export const addMissionInventoryUpdates = async (
|
||||
addShipDecorations(inventory, value);
|
||||
break;
|
||||
case "FusionBundles": {
|
||||
let fusionPointsDelta = 0;
|
||||
let fusionPoints = 0;
|
||||
for (const fusionBundle of value) {
|
||||
fusionPointsDelta += addFusionPoints(
|
||||
inventory,
|
||||
ExportFusionBundles[fusionBundle.ItemType].fusionPoints * fusionBundle.ItemCount
|
||||
);
|
||||
const fusionPointsTotal =
|
||||
ExportFusionBundles[fusionBundle.ItemType].fusionPoints * fusionBundle.ItemCount;
|
||||
inventory.FusionPoints += fusionPointsTotal;
|
||||
fusionPoints += fusionPointsTotal;
|
||||
}
|
||||
inventoryChanges.FusionPoints = fusionPointsDelta;
|
||||
inventoryChanges.FusionPoints = fusionPoints;
|
||||
break;
|
||||
}
|
||||
case "EmailItems": {
|
||||
@ -349,7 +358,6 @@ export const addMissionInventoryUpdates = async (
|
||||
: 10)
|
||||
) {
|
||||
progress.Completed = true;
|
||||
inventory.LibraryPersonalTarget = undefined;
|
||||
}
|
||||
logger.debug(`synthesis of ${scan.EnemyType} added to personal target progress`);
|
||||
synthesisIgnored = false;
|
||||
@ -403,11 +411,6 @@ export const addMissionInventoryUpdates = async (
|
||||
upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress
|
||||
});
|
||||
break;
|
||||
case "WeaponSkins":
|
||||
for (const item of value) {
|
||||
addSkin(inventory, item.ItemType);
|
||||
}
|
||||
break;
|
||||
case "Boosters":
|
||||
value.forEach(booster => {
|
||||
addBooster(booster.ItemType, booster.ExpiryDate, inventory);
|
||||
@ -485,16 +488,6 @@ export const addMissionInventoryUpdates = async (
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "KubrowPetEggs": {
|
||||
for (const egg of value) {
|
||||
inventory.KubrowPetEggs ??= [];
|
||||
inventory.KubrowPetEggs.push({
|
||||
ItemType: egg.ItemType,
|
||||
_id: new Types.ObjectId()
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "DiscoveredMarkers": {
|
||||
for (const clientMarker of value) {
|
||||
const dbMarker = inventory.DiscoveredMarkers.find(x => x.tag == clientMarker.tag);
|
||||
@ -506,23 +499,6 @@ export const addMissionInventoryUpdates = async (
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "BrandedSuits": {
|
||||
inventory.BrandedSuits ??= [];
|
||||
if (!inventory.BrandedSuits.find(x => x.equals(value.$oid))) {
|
||||
inventory.BrandedSuits.push(new Types.ObjectId(value.$oid));
|
||||
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
sndr: "/Lotus/Language/Menu/Mailbox_WarframeSender",
|
||||
msg: "/Lotus/Language/G1Quests/BrandedMessage",
|
||||
sub: "/Lotus/Language/G1Quests/BrandedTitle",
|
||||
att: ["/Lotus/Types/Recipes/Components/BrandRemovalBlueprint"],
|
||||
highPriority: true // TOVERIFY: I cannot find any content of this within the last 10 years so I can only assume that highPriority is set (it certainly would make sense), but I just don't know for sure that it is so on live.
|
||||
}
|
||||
]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "LockedWeaponGroup": {
|
||||
inventory.LockedWeaponGroup = {
|
||||
s: new Types.ObjectId(value.s.$oid),
|
||||
@ -531,17 +507,12 @@ export const addMissionInventoryUpdates = async (
|
||||
m: value.m ? new Types.ObjectId(value.m.$oid) : undefined,
|
||||
sn: value.sn ? new Types.ObjectId(value.sn.$oid) : undefined
|
||||
};
|
||||
inventory.Harvestable = false;
|
||||
break;
|
||||
}
|
||||
case "UnlockWeapons": {
|
||||
inventory.LockedWeaponGroup = undefined;
|
||||
break;
|
||||
}
|
||||
case "IncHarvester": {
|
||||
inventory.Harvestable = true;
|
||||
break;
|
||||
}
|
||||
case "CurrentLoadOutIds": {
|
||||
if (value.LoadOuts) {
|
||||
const loadout = await Loadout.findOne({ loadoutOwnerId: inventory.accountOwnerId });
|
||||
@ -590,79 +561,10 @@ export const addMissionInventoryUpdates = async (
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "CalendarProgress": {
|
||||
const calendarProgress = getCalendarProgress(inventory);
|
||||
for (const progress of value) {
|
||||
const challengeName = progress.challenge.substring(progress.challenge.lastIndexOf("/") + 1);
|
||||
calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx++;
|
||||
calendarProgress.SeasonProgress.ActivatedChallenges.push(challengeName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "duviriCaveOffers": {
|
||||
// Duviri cave offers (generated with the duviri seed) change after completing one of its game modes (not when aborting).
|
||||
if (inventoryUpdates.MissionStatus != "GS_QUIT") {
|
||||
inventory.DuviriInfo!.Seed = generateRewardSeed();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "NemesisKillConvert":
|
||||
if (inventory.Nemesis) {
|
||||
inventory.NemesisHistory ??= [];
|
||||
inventory.NemesisHistory.push({
|
||||
// Copy over all 'base' values
|
||||
fp: inventory.Nemesis.fp,
|
||||
d: inventory.Nemesis.d,
|
||||
manifest: inventory.Nemesis.manifest,
|
||||
KillingSuit: inventory.Nemesis.KillingSuit,
|
||||
killingDamageType: inventory.Nemesis.killingDamageType,
|
||||
ShoulderHelmet: inventory.Nemesis.ShoulderHelmet,
|
||||
WeaponIdx: inventory.Nemesis.WeaponIdx,
|
||||
AgentIdx: inventory.Nemesis.AgentIdx,
|
||||
BirthNode: inventory.Nemesis.BirthNode,
|
||||
Faction: inventory.Nemesis.Faction,
|
||||
Rank: inventory.Nemesis.Rank,
|
||||
Traded: inventory.Nemesis.Traded,
|
||||
PrevOwners: inventory.Nemesis.PrevOwners,
|
||||
SecondInCommand: inventory.Nemesis.SecondInCommand,
|
||||
Weakened: inventory.Nemesis.Weakened,
|
||||
// And set killed flag
|
||||
k: value.killed
|
||||
});
|
||||
|
||||
if (value.killed) {
|
||||
if (
|
||||
value.weaponLoc &&
|
||||
inventory.Nemesis.Faction != "FC_INFESTATION" // weaponLoc is "/Lotus/Language/Weapons/DerelictCernosName" for these for some reason
|
||||
) {
|
||||
const weaponType = getWeaponsForManifest(inventory.Nemesis.manifest)[
|
||||
inventory.Nemesis.WeaponIdx
|
||||
];
|
||||
giveNemesisWeaponRecipe(
|
||||
inventory,
|
||||
weaponType,
|
||||
value.nemesisName,
|
||||
value.weaponLoc,
|
||||
inventory.Nemesis.KillingSuit,
|
||||
inventory.Nemesis.fp
|
||||
);
|
||||
}
|
||||
if (value.petLoc) {
|
||||
giveNemesisPetRecipe(inventory);
|
||||
}
|
||||
}
|
||||
|
||||
// TOVERIFY: Is the inbox message also sent when converting a lich? If not, how are the rewards given?
|
||||
if (inventory.Nemesis.Faction == "FC_INFESTATION") {
|
||||
await sendCodaFinishedMessage(inventory, inventory.Nemesis.fp, value.nemesisName, value.killed);
|
||||
}
|
||||
|
||||
inventory.Nemesis = undefined;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Equipment XP updates
|
||||
if (equipmentKeys.includes(key as TEquipmentKey)) {
|
||||
applyClientEquipmentUpdates(inventory, value as IEquipmentClient[], key as TEquipmentKey);
|
||||
addGearExpByCategory(inventory, value as IEquipmentClient[], key as TEquipmentKey);
|
||||
}
|
||||
break;
|
||||
// if (
|
||||
@ -684,140 +586,8 @@ interface AddMissionRewardsReturnType {
|
||||
credits?: IMissionCredits;
|
||||
AffiliationMods?: IAffiliationMods[];
|
||||
SyndicateXPItemReward?: number;
|
||||
ConquestCompletedMissionsCount?: number;
|
||||
}
|
||||
|
||||
interface IConquestReward {
|
||||
at: number;
|
||||
pool: IRngResult[];
|
||||
}
|
||||
|
||||
const labConquestRewards: IConquestReward[] = [
|
||||
{
|
||||
at: 5,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 10,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 15,
|
||||
pool: [
|
||||
{
|
||||
type: "/Lotus/StoreItems/Types/Gameplay/EntratiLab/Resources/EntratiLanthornBundle",
|
||||
itemCount: 3,
|
||||
probability: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
at: 20,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 28,
|
||||
pool: [
|
||||
{
|
||||
type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints",
|
||||
itemCount: 20,
|
||||
probability: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
at: 31,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 34,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestArcaneRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 37,
|
||||
pool: [
|
||||
{
|
||||
type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints",
|
||||
itemCount: 50,
|
||||
probability: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const hexConquestRewards: IConquestReward[] = [
|
||||
{
|
||||
at: 5,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 10,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 15,
|
||||
pool: [
|
||||
{
|
||||
type: "/Lotus/StoreItems/Types/BoosterPacks/1999StickersPackEchoesArchimedea",
|
||||
itemCount: 1,
|
||||
probability: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
at: 20,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 28,
|
||||
pool: [
|
||||
{
|
||||
type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks",
|
||||
itemCount: 6,
|
||||
probability: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
at: 31,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 34,
|
||||
pool: ExportRewards[
|
||||
"/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestArcaneRewards"
|
||||
][0] as IRngResult[]
|
||||
},
|
||||
{
|
||||
at: 37,
|
||||
pool: [
|
||||
{
|
||||
type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks",
|
||||
itemCount: 9,
|
||||
probability: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
//TODO: return type of partial missioninventoryupdate response
|
||||
export const addMissionRewards = async (
|
||||
inventory: TInventoryDatabaseDocument,
|
||||
@ -839,13 +609,17 @@ export const addMissionRewards = async (
|
||||
return { MissionRewards: [] };
|
||||
}
|
||||
|
||||
if (rewardInfo.rewardSeed) {
|
||||
// We're using a reward seed, so give the client a new one in the response. On live, missionInventoryUpdate seems to always provide a fresh one in the response.
|
||||
inventory.RewardSeed = generateRewardSeed();
|
||||
}
|
||||
|
||||
//TODO: check double reward merging
|
||||
const MissionRewards: IMissionReward[] = getRandomMissionDrops(inventory, rewardInfo, wagerTier, firstCompletion);
|
||||
logger.debug("random mission drops:", MissionRewards);
|
||||
const inventoryChanges: IInventoryChanges = {};
|
||||
const AffiliationMods: IAffiliationMods[] = [];
|
||||
let SyndicateXPItemReward;
|
||||
let ConquestCompletedMissionsCount;
|
||||
|
||||
let missionCompletionCredits = 0;
|
||||
//inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display
|
||||
@ -859,7 +633,8 @@ export const addMissionRewards = async (
|
||||
for (const reward of fixedLevelRewards.levelKeyRewards2) {
|
||||
//quest stage completion credit rewards
|
||||
if (reward.rewardType == "RT_CREDITS") {
|
||||
missionCompletionCredits += reward.amount; // will be added to inventory in addCredits
|
||||
inventory.RegularCredits += reward.amount;
|
||||
missionCompletionCredits += reward.amount;
|
||||
continue;
|
||||
}
|
||||
MissionRewards.push({
|
||||
@ -883,8 +658,7 @@ export const addMissionRewards = async (
|
||||
missions.Tag != "SolNode761" && // the index
|
||||
missions.Tag != "SolNode762" && // the index
|
||||
missions.Tag != "SolNode763" && // the index
|
||||
missions.Tag != "CrewBattleNode556" && // free flight
|
||||
getRotations(rewardInfo).length > 0 // (E)SO should not give credits for only completing zone 1, in which case it has no rewardQualifications (https://onlyg.it/OpenWF/SpaceNinjaServer/issues/1823)
|
||||
missions.Tag != "CrewBattleNode556" // free flight
|
||||
) {
|
||||
const levelCreditReward = getLevelCreditRewards(node);
|
||||
missionCompletionCredits += levelCreditReward;
|
||||
@ -896,14 +670,6 @@ export const addMissionRewards = async (
|
||||
missionCompletionCredits += addFixedLevelRewards(node.missionReward, inventory, MissionRewards, rewardInfo);
|
||||
}
|
||||
|
||||
if (rewardInfo.sortieTag == "Mission1") {
|
||||
missionCompletionCredits += 20_000;
|
||||
} else if (rewardInfo.sortieTag == "Mission2") {
|
||||
missionCompletionCredits += 30_000;
|
||||
} else if (rewardInfo.sortieTag == "Final") {
|
||||
missionCompletionCredits += 50_000;
|
||||
}
|
||||
|
||||
if (missions.Tag == "PlutoToErisJunction") {
|
||||
await createMessage(inventory.accountOwnerId, [
|
||||
{
|
||||
@ -920,75 +686,11 @@ export const addMissionRewards = async (
|
||||
|
||||
if (rewardInfo.useVaultManifest) {
|
||||
MissionRewards.push({
|
||||
StoreItem: getRandomElement(corruptedMods)!,
|
||||
StoreItem: getRandomElement(corruptedMods),
|
||||
ItemCount: 1
|
||||
});
|
||||
}
|
||||
|
||||
if (rewardInfo.periodicMissionTag == "EliteAlert" || rewardInfo.periodicMissionTag == "EliteAlertB") {
|
||||
missionCompletionCredits += 50_000;
|
||||
MissionRewards.push({
|
||||
StoreItem: "/Lotus/StoreItems/Types/Items/MiscItems/Elitium",
|
||||
ItemCount: 1
|
||||
});
|
||||
}
|
||||
|
||||
if (rewardInfo.ConquestCompleted !== undefined) {
|
||||
let score = 1;
|
||||
if (rewardInfo.ConquestHardModeActive === 1) score += 3;
|
||||
|
||||
if (rewardInfo.ConquestPersonalModifiersActive !== undefined)
|
||||
score += rewardInfo.ConquestPersonalModifiersActive;
|
||||
if (rewardInfo.ConquestEquipmentSuggestionsFulfilled !== undefined)
|
||||
score += rewardInfo.ConquestEquipmentSuggestionsFulfilled;
|
||||
|
||||
score *= rewardInfo.ConquestCompleted + 1;
|
||||
|
||||
if (rewardInfo.ConquestCompleted == 2 && rewardInfo.ConquestHardModeActive === 1) score += 1;
|
||||
|
||||
logger.debug(`completed conquest mission ${rewardInfo.ConquestCompleted + 1} for a score of ${score}`);
|
||||
|
||||
const conquestType = rewardInfo.ConquestType;
|
||||
const conquestNode =
|
||||
conquestType == "HexConquest" ? "EchoesHexConquestHardModeUnlocked" : "EntratiLabConquestHardModeUnlocked";
|
||||
if (score >= 25 && inventory.NodeIntrosCompleted.indexOf(conquestNode) == -1)
|
||||
inventory.NodeIntrosCompleted.push(conquestNode);
|
||||
|
||||
if (conquestType == "HexConquest") {
|
||||
inventory.EchoesHexConquestCacheScoreMission ??= 0;
|
||||
if (score > inventory.EchoesHexConquestCacheScoreMission) {
|
||||
for (const reward of hexConquestRewards) {
|
||||
if (score >= reward.at && inventory.EchoesHexConquestCacheScoreMission < reward.at) {
|
||||
const rolled = getRandomReward(reward.pool)!;
|
||||
logger.debug(`rolled hex conquest reward for reaching ${reward.at} points`, rolled);
|
||||
MissionRewards.push({
|
||||
StoreItem: rolled.type,
|
||||
ItemCount: rolled.itemCount
|
||||
});
|
||||
}
|
||||
}
|
||||
inventory.EchoesHexConquestCacheScoreMission = score;
|
||||
}
|
||||
} else {
|
||||
inventory.EntratiLabConquestCacheScoreMission ??= 0;
|
||||
if (score > inventory.EntratiLabConquestCacheScoreMission) {
|
||||
for (const reward of labConquestRewards) {
|
||||
if (score >= reward.at && inventory.EntratiLabConquestCacheScoreMission < reward.at) {
|
||||
const rolled = getRandomReward(reward.pool)!;
|
||||
logger.debug(`rolled lab conquest reward for reaching ${reward.at} points`, rolled);
|
||||
MissionRewards.push({
|
||||
StoreItem: rolled.type,
|
||||
ItemCount: rolled.itemCount
|
||||
});
|
||||
}
|
||||
}
|
||||
inventory.EntratiLabConquestCacheScoreMission = score;
|
||||
}
|
||||
}
|
||||
|
||||
ConquestCompletedMissionsCount = rewardInfo.ConquestCompleted == 2 ? 0 : rewardInfo.ConquestCompleted + 1;
|
||||
}
|
||||
|
||||
for (const reward of MissionRewards) {
|
||||
const inventoryChange = await handleStoreItemAcquisition(
|
||||
reward.StoreItem,
|
||||
@ -1095,16 +797,16 @@ export const addMissionRewards = async (
|
||||
|
||||
if (rewardInfo.JobStage != undefined && rewardInfo.jobId) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [jobType, unkIndex, hubNode, syndicateMissionId, locationTag] = rewardInfo.jobId.split("_");
|
||||
const syndicateMissions: ISyndicateMissionInfo[] = [];
|
||||
pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
|
||||
const syndicateEntry = syndicateMissions.find(m => m._id.$oid === syndicateMissionId);
|
||||
const [jobType, unkIndex, hubNode, syndicateId, locationTag] = rewardInfo.jobId.split("_");
|
||||
const worldState = getWorldState();
|
||||
let syndicateEntry = worldState.SyndicateMissions.find(m => m._id.$oid === syndicateId);
|
||||
if (!syndicateEntry) syndicateEntry = worldState.SyndicateMissions.find(m => m.Tag === syndicateId); // Sometimes syndicateId can be tag
|
||||
if (syndicateEntry && syndicateEntry.Jobs) {
|
||||
let currentJob = syndicateEntry.Jobs[rewardInfo.JobTier!];
|
||||
if (syndicateEntry.Tag === "EntratiSyndicate") {
|
||||
const vault = syndicateEntry.Jobs.find(j => j.locationTag === locationTag);
|
||||
if (vault) currentJob = vault;
|
||||
let medallionAmount = Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1));
|
||||
let medallionAmount = currentJob.xpAmounts[rewardInfo.JobStage];
|
||||
|
||||
if (
|
||||
["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some(
|
||||
@ -1127,11 +829,7 @@ export const addMissionRewards = async (
|
||||
} else {
|
||||
if (rewardInfo.JobTier! >= 0) {
|
||||
AffiliationMods.push(
|
||||
addStanding(
|
||||
inventory,
|
||||
syndicateEntry.Tag,
|
||||
Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1))
|
||||
)
|
||||
addStanding(inventory, syndicateEntry.Tag, currentJob.xpAmounts[rewardInfo.JobStage])
|
||||
);
|
||||
} else {
|
||||
if (jobType.endsWith("Heists/HeistProfitTakerBountyOne") && rewardInfo.JobStage === 2) {
|
||||
@ -1184,16 +882,16 @@ export const addMissionRewards = async (
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
inventoryChanges,
|
||||
MissionRewards,
|
||||
credits,
|
||||
AffiliationMods,
|
||||
SyndicateXPItemReward,
|
||||
ConquestCompletedMissionsCount
|
||||
};
|
||||
return { inventoryChanges, MissionRewards, credits, AffiliationMods, SyndicateXPItemReward };
|
||||
};
|
||||
|
||||
interface IMissionCredits {
|
||||
MissionCredits: number[];
|
||||
CreditBonus: number[];
|
||||
TotalCredits: number[];
|
||||
DailyMissionBonus?: boolean;
|
||||
}
|
||||
|
||||
//creditBonus is not entirely accurate.
|
||||
//TODO: consider ActiveBoosters
|
||||
export const addCredits = (
|
||||
@ -1370,24 +1068,6 @@ function getRandomMissionDrops(
|
||||
// Invasion assassination has Phorid has the boss who should drop Nyx parts
|
||||
// TODO: Check that the invasion faction is indeed FC_INFESTATION once the Invasions in worldState are more dynamic
|
||||
rewardManifests = ["/Lotus/Types/Game/MissionDecks/BossMissionRewards/NyxRewards"];
|
||||
} else if (RewardInfo.sortieId) {
|
||||
// Sortie mission types differ from the underlying node and hence also don't give rewards from the underlying nodes. Assassinations are an exception to this.
|
||||
if (region.missionIndex == 0) {
|
||||
const arr = RewardInfo.sortieId.split("_");
|
||||
let sortieId = arr[1];
|
||||
if (sortieId == "Lite") {
|
||||
sortieId = arr[2];
|
||||
}
|
||||
const sortie = getSortie(idToDay(sortieId));
|
||||
const mission = sortie.Variants.find(x => x.node == arr[0])!;
|
||||
if (mission.missionType == "MT_ASSASSINATION") {
|
||||
rewardManifests = region.rewardManifests;
|
||||
} else {
|
||||
rewardManifests = [];
|
||||
}
|
||||
} else {
|
||||
rewardManifests = [];
|
||||
}
|
||||
} else {
|
||||
rewardManifests = region.rewardManifests;
|
||||
}
|
||||
@ -1396,12 +1076,13 @@ function getRandomMissionDrops(
|
||||
if (RewardInfo.jobId) {
|
||||
if (RewardInfo.JobStage! >= 0) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [jobType, unkIndex, hubNode, syndicateMissionId, locationTag] = RewardInfo.jobId.split("_");
|
||||
const [jobType, unkIndex, hubNode, syndicateId, locationTag] = RewardInfo.jobId.split("_");
|
||||
let isEndlessJob = false;
|
||||
if (syndicateMissionId) {
|
||||
const syndicateMissions: ISyndicateMissionInfo[] = [];
|
||||
pushClassicBounties(syndicateMissions, idToBountyCycle(syndicateMissionId));
|
||||
const syndicateEntry = syndicateMissions.find(m => m._id.$oid === syndicateMissionId);
|
||||
if (syndicateId) {
|
||||
const worldState = getWorldState();
|
||||
let syndicateEntry = worldState.SyndicateMissions.find(m => m._id.$oid === syndicateId);
|
||||
if (!syndicateEntry) syndicateEntry = worldState.SyndicateMissions.find(m => m.Tag === syndicateId);
|
||||
|
||||
if (syndicateEntry && syndicateEntry.Jobs) {
|
||||
let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
|
||||
|
||||
@ -1476,11 +1157,7 @@ function getRandomMissionDrops(
|
||||
}
|
||||
}
|
||||
rewardManifests = [job.rewards];
|
||||
if (job.xpAmounts.length > 1) {
|
||||
rotations = [RewardInfo.JobStage! % (job.xpAmounts.length - 1)];
|
||||
} else {
|
||||
rotations = [0];
|
||||
}
|
||||
rotations = [RewardInfo.JobStage! % (job.xpAmounts.length - 1)];
|
||||
if (
|
||||
RewardInfo.Q &&
|
||||
(RewardInfo.JobStage === job.xpAmounts.length - 1 || job.isVault) &&
|
||||
@ -1539,11 +1216,6 @@ function getRandomMissionDrops(
|
||||
if (rewardManifests.length != 0) {
|
||||
logger.debug(`generating random mission rewards`, { rewardManifests, rotations });
|
||||
}
|
||||
if (RewardInfo.rewardSeed) {
|
||||
if (RewardInfo.rewardSeed != inventory.RewardSeed) {
|
||||
logger.warn(`RewardSeed mismatch:`, { client: RewardInfo.rewardSeed, database: inventory.RewardSeed });
|
||||
}
|
||||
}
|
||||
const rng = new SRng(BigInt(RewardInfo.rewardSeed ?? generateRewardSeed()) ^ 0xffffffffffffffffn);
|
||||
rewardManifests.forEach(name => {
|
||||
const table = ExportRewards[name];
|
||||
@ -1598,9 +1270,9 @@ function getRandomMissionDrops(
|
||||
const drop = getRandomRewardByChance(
|
||||
ExportRewards[
|
||||
[
|
||||
"/Lotus/Types/Game/MissionDecks/PurgatoryMissionRewards/PurgatoryBlueTokenRewards",
|
||||
"/Lotus/Types/Game/MissionDecks/PurgatoryMissionRewards/PurgatoryBlackTokenRewards",
|
||||
"/Lotus/Types/Game/MissionDecks/PurgatoryMissionRewards/PurgatoryGoldTokenRewards",
|
||||
"/Lotus/Types/Game/MissionDecks/PurgatoryMissionRewards/PurgatoryBlackTokenRewards"
|
||||
"/Lotus/Types/Game/MissionDecks/PurgatoryMissionRewards/PurgatoryBlueTokenRewards"
|
||||
][Math.trunc(qualification / 3)]
|
||||
][qualification % 3]
|
||||
);
|
||||
@ -1614,17 +1286,6 @@ function getRandomMissionDrops(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (RewardInfo.periodicMissionTag?.startsWith("KuvaMission")) {
|
||||
const drop = getRandomRewardByChance(
|
||||
ExportRewards[
|
||||
RewardInfo.periodicMissionTag == "KuvaMission6" || RewardInfo.periodicMissionTag == "KuvaMission12"
|
||||
? "/Lotus/Types/Game/MissionDecks/KuvaMissionRewards/KuvaSiphonFloodRewards"
|
||||
: "/Lotus/Types/Game/MissionDecks/KuvaMissionRewards/KuvaSiphonRewards"
|
||||
][0]
|
||||
)!;
|
||||
drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
|
||||
}
|
||||
}
|
||||
return drops;
|
||||
}
|
||||
|
@ -1,14 +1,9 @@
|
||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
|
||||
import { addItem, getInventory } from "@/src/services/inventoryService";
|
||||
import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes";
|
||||
import { IGardeningDatabase } from "../types/shipTypes";
|
||||
import { getRandomElement } from "./rngService";
|
||||
|
||||
export const getPersonalRooms = async (
|
||||
accountId: string,
|
||||
projection?: string
|
||||
): Promise<TPersonalRoomsDatabaseDocument> => {
|
||||
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId }, projection);
|
||||
export const getPersonalRooms = async (accountId: string): Promise<TPersonalRoomsDatabaseDocument> => {
|
||||
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
|
||||
|
||||
if (!personalRooms) {
|
||||
throw new Error(`personal rooms not found for account ${accountId}`);
|
||||
@ -30,64 +25,3 @@ export const updateShipFeature = async (accountId: string, shipFeature: string):
|
||||
await addItem(inventory, shipFeature, -1);
|
||||
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,18 +66,6 @@ export const handlePurchase = async (
|
||||
if (!offer) {
|
||||
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) {
|
||||
handleItemPrices(
|
||||
inventory,
|
||||
@ -182,9 +170,6 @@ export const handlePurchase = async (
|
||||
purchaseResponse.InventoryChanges,
|
||||
updateCurrency(inventory, offer.RegularPrice, false)
|
||||
);
|
||||
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
|
||||
throw new Error(`vendor purchase should not have an expected price`);
|
||||
}
|
||||
|
||||
const invItem: IMiscItem = {
|
||||
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
|
||||
@ -223,10 +208,10 @@ export const handlePurchase = async (
|
||||
purchaseResponse.Standing = [
|
||||
{
|
||||
Tag: syndicateTag,
|
||||
Standing: favour.standingCost * purchaseRequest.PurchaseParams.Quantity
|
||||
Standing: favour.standingCost
|
||||
}
|
||||
];
|
||||
affiliation.Standing -= favour.standingCost * purchaseRequest.PurchaseParams.Quantity;
|
||||
affiliation.Standing -= favour.standingCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,18 +223,12 @@ export const handlePurchase = async (
|
||||
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
|
||||
const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem);
|
||||
if (offer) {
|
||||
if (typeof offer.credits == "number") {
|
||||
if (offer.credits) {
|
||||
combineInventoryChanges(
|
||||
purchaseResponse.InventoryChanges,
|
||||
updateCurrency(inventory, offer.credits, false)
|
||||
);
|
||||
}
|
||||
if (typeof offer.platinum == "number") {
|
||||
combineInventoryChanges(
|
||||
purchaseResponse.InventoryChanges,
|
||||
updateCurrency(inventory, offer.platinum, true)
|
||||
);
|
||||
}
|
||||
if (offer.itemPrices) {
|
||||
handleItemPrices(
|
||||
inventory,
|
||||
@ -260,9 +239,6 @@ export const handlePurchase = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
|
||||
throw new Error(`vendor purchase should not have an expected price`);
|
||||
}
|
||||
break;
|
||||
case 18: {
|
||||
if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) {
|
||||
|
@ -6,7 +6,7 @@ export interface IRngResult {
|
||||
probability: number;
|
||||
}
|
||||
|
||||
export const getRandomElement = <T>(arr: readonly T[]): T | undefined => {
|
||||
export const getRandomElement = <T>(arr: T[]): T => {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
};
|
||||
|
||||
@ -18,10 +18,7 @@ export const getRandomInt = (min: number, max: number): number => {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
};
|
||||
|
||||
export const getRewardAtPercentage = <T extends { probability: number }>(
|
||||
pool: T[],
|
||||
percentage: number
|
||||
): T | undefined => {
|
||||
const getRewardAtPercentage = <T extends { probability: number }>(pool: T[], percentage: number): T | undefined => {
|
||||
if (pool.length == 0) return;
|
||||
|
||||
const totalChance = pool.reduce((accum, item) => accum + item.probability, 0);
|
||||
@ -100,20 +97,12 @@ export class CRng {
|
||||
}
|
||||
|
||||
randomInt(min: number, max: number): number {
|
||||
const diff = max - min;
|
||||
if (diff != 0) {
|
||||
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;
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(this.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
randomElement<T>(arr: readonly T[]): T | undefined {
|
||||
randomElement<T>(arr: T[]): T {
|
||||
return arr[Math.floor(this.random() * arr.length)];
|
||||
}
|
||||
|
||||
@ -145,7 +134,7 @@ export class SRng {
|
||||
return min;
|
||||
}
|
||||
|
||||
randomElement<T>(arr: readonly T[]): T | undefined {
|
||||
randomElement<T>(arr: T[]): T {
|
||||
return arr[this.randomInt(0, arr.length - 1)];
|
||||
}
|
||||
|
||||
|
@ -161,11 +161,6 @@ export const handleInventoryItemConfigChange = async (
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "LotusCustomization": {
|
||||
logger.debug(`saved LotusCustomization`, equipmentChanges.LotusCustomization);
|
||||
inventory.LotusCustomization = equipmentChanges.LotusCustomization;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (equipmentKeys.includes(equipmentName as TEquipmentKey) && equipmentName != "ValidNewLoadoutId") {
|
||||
logger.debug(`general Item config saved of type ${equipmentName}`, {
|
||||
|
@ -1,12 +1,17 @@
|
||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||
import { catBreadHash } from "@/src/helpers/stringHelpers";
|
||||
import { CRng, mixSeeds } from "@/src/services/rngService";
|
||||
import { IMongoDate } from "@/src/types/commonTypes";
|
||||
import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
|
||||
import { ExportVendors, IRange } from "warframe-public-export-plus";
|
||||
import {
|
||||
IItemManifestPreprocessed,
|
||||
IRawVendorManifest,
|
||||
IVendorInfo,
|
||||
IVendorManifestPreprocessed
|
||||
} from "@/src/types/vendorTypes";
|
||||
import { ExportVendors } from "warframe-public-export-plus";
|
||||
|
||||
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
|
||||
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
|
||||
import DeimosFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosFishmongerVendorManifest.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";
|
||||
@ -18,22 +23,26 @@ import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorIn
|
||||
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 GuildAdvertisementVendorManifest from "@/static/fixed_responses/getVendorInfo/GuildAdvertisementVendorManifest.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 OstronFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronFishmongerVendorManifest.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 SolarisDebtTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorManifest.json";
|
||||
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
|
||||
import SolarisFishmongerVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisFishmongerVendorManifest.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: IVendorManifest[] = [
|
||||
const rawVendorManifests: IRawVendorManifest[] = [
|
||||
ArchimedeanVendorManifest,
|
||||
DeimosEntratiFragmentVendorProductsManifest,
|
||||
DeimosFishmongerVendorManifest,
|
||||
DeimosHivemindCommisionsManifestFishmonger,
|
||||
DeimosHivemindCommisionsManifestPetVendor,
|
||||
DeimosHivemindCommisionsManifestProspector,
|
||||
@ -45,22 +54,25 @@ const rawVendorManifests: IVendorManifest[] = [
|
||||
DuviriAcrithisVendorManifest,
|
||||
EntratiLabsEntratiLabsCommisionsManifest,
|
||||
EntratiLabsEntratiLabVendorManifest,
|
||||
GuildAdvertisementVendorManifest, // uses preprocessing
|
||||
HubsIronwakeDondaVendorManifest, // uses preprocessing
|
||||
HubsRailjackCrewMemberVendorManifest,
|
||||
MaskSalesmanManifest,
|
||||
Nova1999ConquestShopManifest,
|
||||
OstronFishmongerVendorManifest,
|
||||
OstronPetVendorManifest,
|
||||
OstronProspectorVendorManifest,
|
||||
RadioLegionIntermission12VendorManifest,
|
||||
SolarisDebtTokenVendorManifest,
|
||||
SolarisDebtTokenVendorRepossessionsManifest,
|
||||
SolarisFishmongerVendorManifest,
|
||||
SolarisProspectorVendorManifest,
|
||||
Temple1999VendorManifest,
|
||||
TeshinHardModeVendorManifest, // uses preprocessing
|
||||
ZarimanCommisionsManifestArchimedean
|
||||
];
|
||||
|
||||
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
|
||||
cycleOffset?: number;
|
||||
cycleStart: number;
|
||||
cycleDuration: number;
|
||||
}
|
||||
|
||||
@ -68,37 +80,30 @@ const generatableVendors: IGeneratableVendorInfo[] = [
|
||||
{
|
||||
_id: { $oid: "67dadc30e4b6e0e5979c8d84" },
|
||||
TypeName: "/Lotus/Types/Game/VendorManifests/TheHex/InfestedLichWeaponVendorManifest",
|
||||
PropertyTextHash: "77093DD05A8561A022DEC9A4B9BB4A56",
|
||||
RandomSeedType: "VRST_WEAPON",
|
||||
RequiredGoalTag: "",
|
||||
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||
cycleOffset: 1740960000_000,
|
||||
cycleStart: 1740960000_000,
|
||||
cycleDuration: 4 * unixTimesInMs.day
|
||||
},
|
||||
{
|
||||
_id: { $oid: "60ad3b6ec96976e97d227e19" },
|
||||
TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/PerrinSequenceWeaponVendorManifest",
|
||||
PropertyTextHash: "34F8CF1DFF745F0D67433A5EF0A03E70",
|
||||
RandomSeedType: "VRST_WEAPON",
|
||||
WeaponUpgradeValueAttenuationExponent: 2.25,
|
||||
cycleOffset: 1744934400_000,
|
||||
cycleStart: 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"
|
||||
// TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest",
|
||||
// PropertyTextHash: "62B64A8065B7C0FA345895D4BC234621"
|
||||
// }
|
||||
];
|
||||
|
||||
const getVendorOid = (typeName: string): string => {
|
||||
return "5be4a159b144f3cd" + catBreadHash(typeName).toString(16).padStart(8, "0");
|
||||
};
|
||||
|
||||
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
|
||||
export const getVendorManifestByTypeName = (typeName: string): IVendorManifestPreprocessed | undefined => {
|
||||
for (const vendorManifest of rawVendorManifests) {
|
||||
if (vendorManifest.VendorInfo.TypeName == typeName) {
|
||||
return preprocessVendorManifest(vendorManifest);
|
||||
@ -109,18 +114,10 @@ export const getVendorManifestByTypeName = (typeName: string): IVendorManifest |
|
||||
return generateVendorManifest(vendorInfo);
|
||||
}
|
||||
}
|
||||
if (typeName in ExportVendors) {
|
||||
return generateVendorManifest({
|
||||
_id: { $oid: getVendorOid(typeName) },
|
||||
TypeName: typeName,
|
||||
RandomSeedType: ExportVendors[typeName].randomSeedType,
|
||||
cycleDuration: unixTimesInMs.hour
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => {
|
||||
export const getVendorManifestByOid = (oid: string): IVendorManifestPreprocessed | undefined => {
|
||||
for (const vendorManifest of rawVendorManifests) {
|
||||
if (vendorManifest.VendorInfo._id.$oid == oid) {
|
||||
return preprocessVendorManifest(vendorManifest);
|
||||
@ -131,34 +128,32 @@ export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined
|
||||
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;
|
||||
};
|
||||
|
||||
const preprocessVendorManifest = (originalManifest: IVendorManifest): IVendorManifest => {
|
||||
const preprocessVendorManifest = (originalManifest: IRawVendorManifest): IVendorManifestPreprocessed => {
|
||||
if (Date.now() >= parseInt(originalManifest.VendorInfo.Expiry.$date.$numberLong)) {
|
||||
const manifest = structuredClone(originalManifest);
|
||||
const info = manifest.VendorInfo;
|
||||
refreshExpiry(info.Expiry);
|
||||
for (const offer of info.ItemManifest) {
|
||||
refreshExpiry(offer.Expiry);
|
||||
const iteration = 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;
|
||||
return manifest as IVendorManifestPreprocessed;
|
||||
}
|
||||
return originalManifest;
|
||||
return originalManifest as IVendorManifestPreprocessed;
|
||||
};
|
||||
|
||||
const refreshExpiry = (expiry: IMongoDate): void => {
|
||||
const refreshExpiry = (expiry: IMongoDate): number => {
|
||||
const period = parseInt(expiry.$date.$numberLong);
|
||||
if (Date.now() >= period) {
|
||||
const epoch = 1734307200_000; // Monday (for weekly schedules)
|
||||
@ -166,143 +161,65 @@ const refreshExpiry = (expiry: IMongoDate): void => {
|
||||
const start = epoch + iteration * period;
|
||||
const end = start + period;
|
||||
expiry.$date.$numberLong = end.toString();
|
||||
return iteration;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const toRange = (value: IRange | number): IRange => {
|
||||
if (typeof value == "number") {
|
||||
return { minValue: value, maxValue: value };
|
||||
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifestPreprocessed => {
|
||||
const EPOCH = vendorInfo.cycleStart;
|
||||
const manifest = ExportVendors[vendorInfo.TypeName];
|
||||
let binThisCycle;
|
||||
if (manifest.isOneBinPerCycle) {
|
||||
const cycleDuration = vendorInfo.cycleDuration; // manifest.items[0].durationHours! * 3600_000;
|
||||
const cycleIndex = Math.trunc((Date.now() - EPOCH) / cycleDuration);
|
||||
binThisCycle = cycleIndex % 2; // Note: May want to auto-compute the bin size, but this is only used for coda weapons right now.
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const vendorInfoCache: Record<string, IVendorInfo> = {};
|
||||
|
||||
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => {
|
||||
if (!(vendorInfo.TypeName in vendorInfoCache)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
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;
|
||||
}
|
||||
const items: IItemManifestPreprocessed[] = [];
|
||||
let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER;
|
||||
for (let i = 0; i != manifest.items.length; ++i) {
|
||||
const rawItem = manifest.items[i];
|
||||
if (manifest.isOneBinPerCycle && rawItem.bin != binThisCycle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add new offers
|
||||
const vendorSeed = parseInt(vendorInfo._id.$oid.substring(16), 16);
|
||||
const cycleOffset = vendorInfo.cycleOffset ?? 1734307200_000;
|
||||
const cycleDuration = vendorInfo.cycleDuration;
|
||||
const cycleIndex = Math.trunc((Date.now() - cycleOffset) / cycleDuration);
|
||||
const rng = new CRng(mixSeeds(vendorSeed, cycleIndex));
|
||||
const manifest = ExportVendors[vendorInfo.TypeName];
|
||||
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 cycleDuration = vendorInfo.cycleDuration; // rawItem.durationHours! * 3600_000;
|
||||
const cycleIndex = Math.trunc((Date.now() - EPOCH) / cycleDuration);
|
||||
const cycleStart = EPOCH + cycleIndex * cycleDuration;
|
||||
const cycleEnd = cycleStart + cycleDuration;
|
||||
if (soonestOfferExpiry > cycleEnd) {
|
||||
soonestOfferExpiry = cycleEnd;
|
||||
}
|
||||
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 = {
|
||||
const rng = new CRng(cycleIndex);
|
||||
rng.churnSeed(i);
|
||||
/*for (let j = -1; j != rawItem.duplicates; ++j)*/ {
|
||||
const item: IItemManifestPreprocessed = {
|
||||
StoreItem: rawItem.storeItem,
|
||||
ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
|
||||
ItemPrices: rawItem.itemPrices!.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
|
||||
Bin: "BIN_" + rawItem.bin,
|
||||
QuantityMultiplier: 1,
|
||||
Expiry: { $date: { $numberLong: expiry.toString() } },
|
||||
Expiry: { $date: { $numberLong: cycleEnd.toString() } },
|
||||
AllowMultipurchase: false,
|
||||
Id: {
|
||||
$oid:
|
||||
((cycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
|
||||
i.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")
|
||||
rng.randomInt(0, 0xffffffff).toString(16).padStart(8, "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);
|
||||
}
|
||||
item.LocTagRandSeed =
|
||||
(BigInt(rng.randomInt(0, 0xffffffff)) << 32n) | BigInt(rng.randomInt(0, 0xffffffff));
|
||||
}
|
||||
processed.ItemManifest.push(item);
|
||||
items.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();
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { cycleStart, cycleDuration, ...clientVendorInfo } = vendorInfo;
|
||||
return {
|
||||
VendorInfo: processed
|
||||
VendorInfo: {
|
||||
...clientVendorInfo,
|
||||
ItemManifest: items,
|
||||
Expiry: { $date: { $numberLong: soonestOfferExpiry.toString() } }
|
||||
}
|
||||
};
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
||||
import { Types } from "mongoose";
|
||||
import { IMongoDate, IOid } from "./commonTypes";
|
||||
|
||||
export interface IFriendInfo {
|
||||
_id: IOid;
|
||||
DisplayName?: string;
|
||||
PlatformNames?: string[];
|
||||
PlatformAccountId?: string;
|
||||
Status?: number;
|
||||
ActiveAvatarImageType?: string;
|
||||
LastLogin?: IMongoDate;
|
||||
PlayerLevel?: number;
|
||||
Suffix?: number;
|
||||
Note?: string;
|
||||
Favorite?: boolean;
|
||||
NewRequest?: boolean;
|
||||
}
|
||||
|
||||
export interface IFriendship {
|
||||
owner: Types.ObjectId;
|
||||
friend: Types.ObjectId;
|
||||
Note?: string;
|
||||
Favorite?: boolean;
|
||||
}
|
@ -2,7 +2,6 @@ import { Types } from "mongoose";
|
||||
import { IOid, IMongoDate } from "@/src/types/commonTypes";
|
||||
import { IFusionTreasure, IMiscItem, ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
|
||||
import { IPictureFrameInfo } from "./shipTypes";
|
||||
import { IFriendInfo } from "./friendTypes";
|
||||
|
||||
export interface IGuildClient {
|
||||
_id: IOid;
|
||||
@ -12,7 +11,6 @@ export interface IGuildClient {
|
||||
Members: IGuildMemberClient[];
|
||||
Ranks: IGuildRank[];
|
||||
Tier: number;
|
||||
Emblem?: boolean;
|
||||
Vault: IGuildVault;
|
||||
ActiveDojoColorResearch: string;
|
||||
Class: number;
|
||||
@ -105,6 +103,21 @@ export interface IGuildMemberDatabase {
|
||||
ShipDecorationsContributed?: ITypeCount[];
|
||||
}
|
||||
|
||||
export interface IFriendInfo {
|
||||
_id: IOid;
|
||||
DisplayName?: string;
|
||||
PlatformNames?: string[];
|
||||
PlatformAccountId?: string;
|
||||
Status?: number;
|
||||
ActiveAvatarImageType?: string;
|
||||
LastLogin?: IMongoDate;
|
||||
PlayerLevel?: number;
|
||||
Suffix?: number;
|
||||
Note?: string;
|
||||
Favorite?: boolean;
|
||||
NewRequest?: boolean;
|
||||
}
|
||||
|
||||
// GuildMemberInfo
|
||||
export interface IGuildMemberClient extends IFriendInfo {
|
||||
Rank: number;
|
||||
@ -159,11 +172,9 @@ export interface IDojoComponentClient {
|
||||
Message?: string;
|
||||
RegularCredits?: number; // "Collecting Materials" state: Number of credits that were donated.
|
||||
MiscItems?: IMiscItem[]; // "Collecting Materials" state: Resources that were donated.
|
||||
CompletionTime?: IMongoDate; // new versions
|
||||
TimeRemaining?: number; // old versions
|
||||
CompletionTime?: IMongoDate;
|
||||
RushPlatinum?: number;
|
||||
DestructionTime?: IMongoDate; // new versions
|
||||
DestructionTimeRemaining?: number; // old versions
|
||||
DestructionTime?: IMongoDate;
|
||||
Decos?: IDojoDecoClient[];
|
||||
DecoCapacity?: number;
|
||||
PaintBot?: IOid;
|
||||
@ -195,13 +206,11 @@ export interface IDojoDecoClient {
|
||||
Type: string;
|
||||
Pos: number[];
|
||||
Rot: number[];
|
||||
Scale?: number;
|
||||
Name?: string; // for teleporters
|
||||
Sockets?: number;
|
||||
RegularCredits?: number;
|
||||
MiscItems?: IMiscItem[];
|
||||
CompletionTime?: IMongoDate; // new versions
|
||||
TimeRemaining?: number; // old versions
|
||||
CompletionTime?: IMongoDate;
|
||||
RushPlatinum?: number;
|
||||
PictureFrameInfo?: IPictureFrameInfo;
|
||||
Pending?: boolean;
|
||||
|
@ -11,7 +11,6 @@ import {
|
||||
IOperatorConfigDatabase
|
||||
} from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { IFingerprintStat, RivenFingerprint } from "@/src/helpers/rivenHelper";
|
||||
import { IOrbiter } from "../personalRoomsTypes";
|
||||
|
||||
export type InventoryDatabaseEquipment = {
|
||||
[_ in TEquipmentKey]: IEquipmentDatabase[];
|
||||
@ -44,7 +43,6 @@ export interface IInventoryDatabase
|
||||
| "RecentVendorPurchases"
|
||||
| "NextRefill"
|
||||
| "Nemesis"
|
||||
| "NemesisHistory"
|
||||
| "EntratiVaultCountResetDate"
|
||||
| "BrandedSuits"
|
||||
| "LockedWeaponGroup"
|
||||
@ -53,7 +51,6 @@ export interface IInventoryDatabase
|
||||
| "LastLiteSortieReward"
|
||||
| "CrewMembers"
|
||||
| "QualifyingInvasions"
|
||||
| "LastInventorySync"
|
||||
| TEquipmentKey
|
||||
>,
|
||||
InventoryDatabaseEquipment {
|
||||
@ -82,7 +79,6 @@ export interface IInventoryDatabase
|
||||
RecentVendorPurchases?: IRecentVendorPurchaseDatabase[];
|
||||
NextRefill?: Date;
|
||||
Nemesis?: INemesisDatabase;
|
||||
NemesisHistory?: INemesisBaseDatabase[];
|
||||
EntratiVaultCountResetDate?: Date;
|
||||
BrandedSuits?: Types.ObjectId[];
|
||||
LockedWeaponGroup?: ILockedWeaponGroupDatabase;
|
||||
@ -91,7 +87,6 @@ export interface IInventoryDatabase
|
||||
LastLiteSortieReward?: ILastSortieRewardDatabase[];
|
||||
CrewMembers: ICrewMemberDatabase[];
|
||||
QualifyingInvasions: IInvasionProgressDatabase[];
|
||||
LastInventorySync?: Types.ObjectId;
|
||||
}
|
||||
|
||||
export interface IQuestKeyDatabase {
|
||||
@ -139,7 +134,7 @@ export const equipmentKeys = [
|
||||
export type TEquipmentKey = (typeof equipmentKeys)[number];
|
||||
|
||||
export interface IDuviriInfo {
|
||||
Seed: bigint;
|
||||
Seed: number;
|
||||
NumCompletions: number;
|
||||
}
|
||||
|
||||
@ -203,11 +198,11 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
OperatorLoadOuts: IOperatorConfigClient[];
|
||||
KahlLoadOuts: IOperatorConfigClient[];
|
||||
|
||||
DuviriInfo?: IDuviriInfo;
|
||||
DuviriInfo: IDuviriInfo;
|
||||
Mailbox?: IMailboxClient;
|
||||
SubscribedToEmails: number;
|
||||
Created: IMongoDate;
|
||||
RewardSeed: bigint;
|
||||
RewardSeed: number | bigint;
|
||||
RegularCredits: number;
|
||||
PremiumCredits: number;
|
||||
PremiumCreditsFree: number;
|
||||
@ -261,7 +256,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
EquippedGear: string[];
|
||||
DeathMarks: string[];
|
||||
FusionTreasures: IFusionTreasure[];
|
||||
//WebFlags: IWebFlags;
|
||||
WebFlags: IWebFlags;
|
||||
CompletedAlerts: string[];
|
||||
Consumables: ITypeCount[];
|
||||
LevelKeys: ITypeCount[];
|
||||
@ -271,10 +266,10 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
KubrowPetEggs?: IKubrowPetEggClient[];
|
||||
LoreFragmentScans: ILoreFragmentScan[];
|
||||
EquippedEmotes: string[];
|
||||
//PendingTrades: IPendingTrade[];
|
||||
PendingTrades: IPendingTrade[];
|
||||
Boosters: IBooster[];
|
||||
ActiveDojoColorResearch: string;
|
||||
//SentientSpawnChanceBoosters: ISentientSpawnChanceBoosters;
|
||||
SentientSpawnChanceBoosters: ISentientSpawnChanceBoosters;
|
||||
SupportedSyndicate?: string;
|
||||
Affiliations: IAffiliation[];
|
||||
QualifyingInvasions: IInvasionProgressClient[];
|
||||
@ -296,19 +291,19 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
ActiveAvatarImageType: string;
|
||||
ShipDecorations: ITypeCount[];
|
||||
DiscoveredMarkers: IDiscoveredMarker[];
|
||||
//CompletedJobs: ICompletedJob[];
|
||||
CompletedJobs: ICompletedJob[];
|
||||
FocusAbility?: string;
|
||||
FocusUpgrades: IFocusUpgrade[];
|
||||
HasContributedToDojo?: boolean;
|
||||
HWIDProtectEnabled?: boolean;
|
||||
//KubrowPetPrints: IKubrowPetPrint[];
|
||||
KubrowPetPrints: IKubrowPetPrint[];
|
||||
AlignmentReplay?: IAlignment;
|
||||
//PersonalGoalProgress: IPersonalGoalProgress[];
|
||||
PersonalGoalProgress: IPersonalGoalProgress[];
|
||||
ThemeStyle: string;
|
||||
ThemeBackground: string;
|
||||
ThemeSounds: string;
|
||||
BountyScore: number;
|
||||
//ChallengeInstanceStates: IChallengeInstanceState[];
|
||||
ChallengeInstanceStates: IChallengeInstanceState[];
|
||||
LoginMilestoneRewards: string[];
|
||||
RecentVendorPurchases?: IRecentVendorPurchaseClient[];
|
||||
NodeIntrosCompleted: string[];
|
||||
@ -316,37 +311,37 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
CompletedJobChains?: ICompletedJobChain[];
|
||||
SeasonChallengeHistory: ISeasonChallenge[];
|
||||
EquippedInstrument?: string;
|
||||
//InvasionChainProgress: IInvasionChainProgress[];
|
||||
InvasionChainProgress: IInvasionChainProgress[];
|
||||
Nemesis?: INemesisClient;
|
||||
NemesisHistory?: INemesisBaseClient[];
|
||||
//LastNemesisAllySpawnTime?: IMongoDate;
|
||||
NemesisHistory: INemesisBaseClient[];
|
||||
LastNemesisAllySpawnTime?: IMongoDate;
|
||||
Settings?: ISettings;
|
||||
PersonalTechProjects: IPersonalTechProjectClient[];
|
||||
PlayerSkills: IPlayerSkills;
|
||||
CrewShipAmmo: ITypeCount[];
|
||||
CrewShipWeaponSkins: IUpgradeClient[];
|
||||
CrewShipSalvagedWeaponSkins: IUpgradeClient[];
|
||||
//TradeBannedUntil?: IMongoDate;
|
||||
TradeBannedUntil?: IMongoDate;
|
||||
PlayedParkourTutorial: boolean;
|
||||
SubscribedToEmailsPersonalized: number;
|
||||
InfestedFoundry?: IInfestedFoundryClient;
|
||||
BlessingCooldown?: IMongoDate;
|
||||
CrewShipRawSalvage: ITypeCount[];
|
||||
CrewMembers: ICrewMemberClient[];
|
||||
LotusCustomization?: ILotusCustomization;
|
||||
LotusCustomization: ILotusCustomization;
|
||||
UseAdultOperatorLoadout?: boolean;
|
||||
NemesisAbandonedRewards: string[];
|
||||
LastInventorySync?: IOid;
|
||||
LastInventorySync: IOid;
|
||||
NextRefill?: IMongoDate;
|
||||
FoundToday?: IMiscItem[]; // for Argon Crystals
|
||||
CustomMarkers?: ICustomMarkers[];
|
||||
//ActiveLandscapeTraps: any[];
|
||||
ActiveLandscapeTraps: any[];
|
||||
EvolutionProgress?: IEvolutionProgress[];
|
||||
//RepVotes: any[];
|
||||
//LeagueTickets: any[];
|
||||
//Quests: any[];
|
||||
//Robotics: any[];
|
||||
//UsedDailyDeals: any[];
|
||||
RepVotes: any[];
|
||||
LeagueTickets: any[];
|
||||
Quests: any[];
|
||||
Robotics: any[];
|
||||
UsedDailyDeals: any[];
|
||||
LibraryPersonalTarget?: string;
|
||||
LibraryPersonalProgress: ILibraryPersonalProgress[];
|
||||
CollectibleSeries?: ICollectibleEntry[];
|
||||
@ -358,7 +353,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
DeathSquadable: boolean;
|
||||
EndlessXP?: IEndlessXpProgress[];
|
||||
DialogueHistory?: IDialogueHistoryClient;
|
||||
CalendarProgress?: ICalendarProgress;
|
||||
CalendarProgress: ICalendarProgress;
|
||||
SongChallenges?: ISongChallenge[];
|
||||
EntratiVaultCountLastPeriod?: number;
|
||||
EntratiVaultCountResetDate?: IMongoDate;
|
||||
@ -374,7 +369,6 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
|
||||
BrandedSuits?: IOid[];
|
||||
LockedWeaponGroup?: ILockedWeaponGroupClient;
|
||||
HubNpcCustomizations?: IHubNpcCustomization[];
|
||||
Ship?: IOrbiter; // U22 and below, response only
|
||||
}
|
||||
|
||||
export interface IAffiliation {
|
||||
@ -560,7 +554,7 @@ export interface ICrewShipCustomization {
|
||||
|
||||
export interface IShipExterior {
|
||||
SkinFlavourItem?: string;
|
||||
Colors?: IColor;
|
||||
Colors: IColor;
|
||||
ShipAttachments?: IShipAttachments;
|
||||
}
|
||||
|
||||
@ -851,7 +845,7 @@ export interface IMission extends IMissionDatabase {
|
||||
}
|
||||
|
||||
export interface INemesisBaseClient {
|
||||
fp: bigint | number;
|
||||
fp: bigint;
|
||||
manifest: string;
|
||||
KillingSuit: string;
|
||||
killingDamageType: number;
|
||||
@ -869,8 +863,7 @@ export interface INemesisBaseClient {
|
||||
Weakened: boolean;
|
||||
}
|
||||
|
||||
export interface INemesisBaseDatabase extends Omit<INemesisBaseClient, "fp" | "d"> {
|
||||
fp: bigint;
|
||||
export interface INemesisBaseDatabase extends Omit<INemesisBaseClient, "d"> {
|
||||
d: Date;
|
||||
}
|
||||
|
||||
@ -884,8 +877,7 @@ export interface INemesisClient extends INemesisBaseClient {
|
||||
LastEnc: number;
|
||||
}
|
||||
|
||||
export interface INemesisDatabase extends Omit<INemesisClient, "fp" | "d"> {
|
||||
fp: bigint;
|
||||
export interface INemesisDatabase extends Omit<INemesisClient, "d"> {
|
||||
d: Date;
|
||||
}
|
||||
|
||||
@ -908,8 +900,8 @@ export interface IPendingRecipeDatabase {
|
||||
ItemType: string;
|
||||
CompletionDate: Date;
|
||||
ItemId: IOid;
|
||||
TargetItemId?: string; // unsure what this is for
|
||||
TargetFingerprint?: string;
|
||||
TargetItemId?: string; // likely related to liches
|
||||
TargetFingerprint?: string; // likely related to liches
|
||||
LongGuns?: IEquipmentDatabase[];
|
||||
Pistols?: IEquipmentDatabase[];
|
||||
Melee?: IEquipmentDatabase[];
|
||||
@ -957,17 +949,6 @@ export interface ICrewShipComponentFingerprint extends IInnateDamageFingerprint
|
||||
SubroutineIndex?: number;
|
||||
}
|
||||
|
||||
export interface INemesisWeaponTargetFingerprint {
|
||||
ItemType: string;
|
||||
UpgradeFingerprint: IInnateDamageFingerprint;
|
||||
Name: string;
|
||||
}
|
||||
|
||||
export interface INemesisPetTargetFingerprint {
|
||||
Parts: string[];
|
||||
Name: string;
|
||||
}
|
||||
|
||||
export enum GettingSlotOrderInfo {
|
||||
Empty = "",
|
||||
LotusUpgradesModsRandomizedPlayerMeleeWeaponRandomModRare0 = "/Lotus/Upgrades/Mods/Randomized/PlayerMeleeWeaponRandomModRare:0",
|
||||
@ -1212,18 +1193,17 @@ export interface IMarker {
|
||||
z: number;
|
||||
showInHud: boolean;
|
||||
}
|
||||
|
||||
export interface ISeasonProgress {
|
||||
SeasonType: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
|
||||
SeasonType: "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
|
||||
LastCompletedDayIdx: number;
|
||||
LastCompletedChallengeDayIdx: number;
|
||||
ActivatedChallenges: string[];
|
||||
ActivatedChallenges: unknown[];
|
||||
}
|
||||
|
||||
export interface ICalendarProgress {
|
||||
Version: number;
|
||||
Iteration: number;
|
||||
YearProgress: { Upgrades: string[] };
|
||||
YearProgress: { Upgrades: unknown[] };
|
||||
SeasonProgress: ISeasonProgress;
|
||||
}
|
||||
|
||||
|
@ -3,21 +3,19 @@ import { Types } from "mongoose";
|
||||
export interface IAccountAndLoginResponseCommons {
|
||||
DisplayName: string;
|
||||
CountryCode: string;
|
||||
ClientType?: string;
|
||||
CrossPlatformAllowed?: boolean;
|
||||
ForceLogoutVersion?: number;
|
||||
ClientType: string;
|
||||
CrossPlatformAllowed: boolean;
|
||||
ForceLogoutVersion: number;
|
||||
AmazonAuthToken?: string;
|
||||
AmazonRefreshToken?: string;
|
||||
ConsentNeeded?: boolean;
|
||||
TrackedSettings?: string[];
|
||||
ConsentNeeded: boolean;
|
||||
TrackedSettings: string[];
|
||||
Nonce: number;
|
||||
}
|
||||
|
||||
export interface IDatabaseAccountRequiredFields extends IAccountAndLoginResponseCommons {
|
||||
email: string;
|
||||
password: string;
|
||||
BuildLabel?: string;
|
||||
LastLogin: Date;
|
||||
}
|
||||
|
||||
export interface IDatabaseAccount extends IDatabaseAccountRequiredFields {
|
||||
@ -36,23 +34,23 @@ export interface ILoginRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
time: number;
|
||||
s?: string;
|
||||
lang?: string;
|
||||
s: string;
|
||||
lang: string;
|
||||
date: number;
|
||||
ClientType?: string;
|
||||
PS?: string;
|
||||
ClientType: string;
|
||||
PS: string;
|
||||
kick?: boolean;
|
||||
}
|
||||
|
||||
export interface ILoginResponse extends IAccountAndLoginResponseCommons {
|
||||
id: string;
|
||||
Groups?: IGroup[];
|
||||
Groups: IGroup[];
|
||||
BuildLabel: string;
|
||||
MatchmakingBuildId?: string;
|
||||
MatchmakingBuildId: string;
|
||||
platformCDNs?: string[];
|
||||
NRS?: string[];
|
||||
DTLS?: number;
|
||||
IRC?: string[];
|
||||
DTLS: number;
|
||||
IRC: string[];
|
||||
HUB?: string;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { IAffiliationMods, IInventoryChanges } from "./purchaseTypes";
|
||||
|
||||
export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
|
||||
export type IInventoryFieldType = (typeof inventoryFields)[number];
|
||||
|
||||
@ -8,27 +6,8 @@ export interface IMissionReward {
|
||||
TypeName?: string;
|
||||
UpgradeLevel?: number;
|
||||
ItemCount: number;
|
||||
DailyCooldown?: boolean;
|
||||
Rarity?: number;
|
||||
TweetText?: string;
|
||||
ProductCategory?: string;
|
||||
FromEnemyCache?: boolean;
|
||||
IsStrippedItem?: boolean;
|
||||
}
|
||||
|
||||
export interface IMissionCredits {
|
||||
MissionCredits: number[];
|
||||
CreditBonus: number[];
|
||||
TotalCredits: number[];
|
||||
DailyMissionBonus?: boolean;
|
||||
}
|
||||
|
||||
export interface IMissionInventoryUpdateResponse extends Partial<IMissionCredits> {
|
||||
ConquestCompletedMissionsCount?: number;
|
||||
InventoryJson?: string;
|
||||
MissionRewards?: IMissionReward[];
|
||||
InventoryChanges?: IInventoryChanges;
|
||||
FusionPoints?: number;
|
||||
SyndicateXPItemReward?: number;
|
||||
AffiliationMods?: IAffiliationMods[];
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { IColor } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import {
|
||||
IApartment,
|
||||
IRoom,
|
||||
IPlacedDecosDatabase,
|
||||
ITailorShop,
|
||||
ITailorShopDatabase,
|
||||
TBootLocation,
|
||||
IApartmentDatabase,
|
||||
IApartmentClient
|
||||
IApartmentDatabase
|
||||
} from "@/src/types/shipTypes";
|
||||
import { Document, Model, Types } from "mongoose";
|
||||
|
||||
@ -21,10 +21,10 @@ export interface IOrbiter {
|
||||
BootLocation?: TBootLocation;
|
||||
}
|
||||
|
||||
export interface IPersonalRoomsClient {
|
||||
export interface IPersonalRooms {
|
||||
ShipInteriorColors: IColor;
|
||||
Ship: IOrbiter;
|
||||
Apartment: IApartmentClient;
|
||||
Apartment: IApartment;
|
||||
TailorShop: ITailorShop;
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,6 @@ export const slotNames = [
|
||||
"WeaponBin",
|
||||
"MechBin",
|
||||
"PveBonusLoadoutBin",
|
||||
"PvpBonusLoadoutBin",
|
||||
"SentinelBin",
|
||||
"SpaceSuitBin",
|
||||
"SpaceWeaponBin",
|
||||
|
@ -20,10 +20,7 @@ import {
|
||||
IDiscoveredMarker,
|
||||
ILockedWeaponGroupClient,
|
||||
ILoadOutPresets,
|
||||
IInvasionProgressClient,
|
||||
IWeaponSkinClient,
|
||||
IKubrowPetEggClient,
|
||||
INemesisClient
|
||||
IInvasionProgressClient
|
||||
} from "./inventoryTypes/inventoryTypes";
|
||||
import { IGroup } from "./loginTypes";
|
||||
|
||||
@ -47,7 +44,6 @@ export type IMissionInventoryUpdateRequest = {
|
||||
|
||||
SyndicateId?: string;
|
||||
SortieId?: string;
|
||||
CalendarProgress?: { challenge: string }[];
|
||||
SeasonChallengeCompletions?: ISeasonChallenge[];
|
||||
AffiliationChanges?: IAffiliationChange[];
|
||||
crossPlaySetting?: string;
|
||||
@ -75,14 +71,6 @@ export type IMissionInventoryUpdateRequest = {
|
||||
PS: string;
|
||||
ActiveDojoColorResearch: string;
|
||||
RewardInfo?: IRewardInfo;
|
||||
NemesisKillConvert?: {
|
||||
nemesisName: string;
|
||||
weaponLoc: string;
|
||||
petLoc: "" | "/Lotus/Language/Pets/ZanukaPetName";
|
||||
fingerprint: bigint | number;
|
||||
killed: boolean;
|
||||
};
|
||||
target?: INemesisClient;
|
||||
ReceivedCeremonyMsg: boolean;
|
||||
LastCeremonyResetDate: number;
|
||||
MissionPTS: number;
|
||||
@ -112,7 +100,6 @@ export type IMissionInventoryUpdateRequest = {
|
||||
}[];
|
||||
CollectibleScans?: ICollectibleEntry[];
|
||||
Upgrades?: IUpgradeClient[]; // riven challenge progress
|
||||
WeaponSkins?: IWeaponSkinClient[];
|
||||
StrippedItems?: {
|
||||
DropTable: string;
|
||||
DROP_MOD?: number[];
|
||||
@ -128,9 +115,7 @@ export type IMissionInventoryUpdateRequest = {
|
||||
NumExtraRewards: number;
|
||||
Count: number;
|
||||
}[];
|
||||
KubrowPetEggs?: IKubrowPetEggClient[];
|
||||
DiscoveredMarkers?: IDiscoveredMarker[];
|
||||
BrandedSuits?: IOid; // sent when captured by g3
|
||||
LockedWeaponGroup?: ILockedWeaponGroupClient; // sent when captured by zanuka
|
||||
UnlockWeapons?: boolean; // sent when recovered weapons from zanuka capture
|
||||
IncHarvester?: boolean; // sent when recovered weapons from zanuka capture
|
||||
@ -140,16 +125,6 @@ export type IMissionInventoryUpdateRequest = {
|
||||
wagerTier?: number; // the index
|
||||
creditsFee?: number; // the index
|
||||
InvasionProgress?: IInvasionProgressClient[];
|
||||
ConquestMissionsCompleted?: number;
|
||||
duviriSuitSelection?: string;
|
||||
duviriPistolSelection?: string;
|
||||
duviriLongGunSelection?: string;
|
||||
duviriMeleeSelection?: string;
|
||||
duviriCaveOffers?: {
|
||||
Seed: number | bigint;
|
||||
Warframes: string[];
|
||||
Weapons: string[];
|
||||
};
|
||||
} & {
|
||||
[K in TEquipmentKey]?: IEquipmentClient[];
|
||||
};
|
||||
@ -159,7 +134,7 @@ export interface IRewardInfo {
|
||||
invasionId?: string;
|
||||
invasionAllyFaction?: "FC_GRINEER" | "FC_CORPUS";
|
||||
sortieId?: string;
|
||||
sortieTag?: "Mission1" | "Mission2" | "Final";
|
||||
sortieTag?: string;
|
||||
sortiePrereqs?: string[];
|
||||
VaultsCracked?: number; // for Spy missions
|
||||
rewardTier?: number;
|
||||
@ -170,19 +145,12 @@ export interface IRewardInfo {
|
||||
lostTargetWave?: number;
|
||||
defenseTargetCount?: number;
|
||||
NemesisAbandonedRewards?: string[];
|
||||
NemesisHenchmenKills?: number;
|
||||
NemesisHintProgress?: number;
|
||||
EOM_AFK?: number;
|
||||
rewardQualifications?: string; // did a Survival for 5 minutes and this was "1"
|
||||
PurgatoryRewardQualifications?: string;
|
||||
rewardSeed?: number | bigint;
|
||||
periodicMissionTag?: string;
|
||||
ConquestType?: string;
|
||||
ConquestCompleted?: number;
|
||||
ConquestEquipmentSuggestionsFulfilled?: number;
|
||||
ConquestPersonalModifiersActive?: number;
|
||||
ConquestStickersActive?: number;
|
||||
ConquestHardModeActive?: number;
|
||||
|
||||
// for bounties, only EOM_AFK and node are given from above, plus:
|
||||
JobTier?: number;
|
||||
jobId?: string;
|
||||
|
@ -6,8 +6,7 @@ import {
|
||||
ICrewShipMembersClient,
|
||||
ICrewShipWeapon,
|
||||
IFlavourItem,
|
||||
ILoadoutConfigClient,
|
||||
ILotusCustomization
|
||||
ILoadoutConfigClient
|
||||
} from "./inventoryTypes/inventoryTypes";
|
||||
|
||||
export interface ISaveLoadoutRequest {
|
||||
@ -44,7 +43,6 @@ export interface ISaveLoadoutRequest {
|
||||
EquippedEmotes: string[];
|
||||
UseAdultOperatorLoadout: boolean;
|
||||
WeaponSkins: IItemEntry;
|
||||
LotusCustomization: ILotusCustomization;
|
||||
}
|
||||
|
||||
export type ISaveLoadoutRequestNoUpgradeVer = Omit<ISaveLoadoutRequest, "UpgradeVer">;
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Types } from "mongoose";
|
||||
import { IMongoDate, IOid } from "@/src/types/commonTypes";
|
||||
import { IOid } from "@/src/types/commonTypes";
|
||||
import { IColor } from "@/src/types/inventoryTypes/commonInventoryTypes";
|
||||
import { ILoadoutClient } from "./saveLoadoutTypes";
|
||||
|
||||
export interface IGetShipResponse {
|
||||
ShipOwnerId: string;
|
||||
Ship: IShip;
|
||||
Apartment: IApartmentClient;
|
||||
Apartment: IApartment;
|
||||
TailorShop: ITailorShop;
|
||||
LoadOutInventory: { LoadOutPresets: ILoadoutClient };
|
||||
}
|
||||
@ -51,42 +51,28 @@ export interface IRoom {
|
||||
PlacedDecos?: IPlacedDecosDatabase[];
|
||||
}
|
||||
|
||||
export interface IPlantClient {
|
||||
export interface IPlants {
|
||||
PlantType: string;
|
||||
EndTime: IMongoDate;
|
||||
EndTime: IOid;
|
||||
PlotIndex: number;
|
||||
}
|
||||
|
||||
export interface IPlantDatabase extends Omit<IPlantClient, "EndTime"> {
|
||||
EndTime: Date;
|
||||
}
|
||||
|
||||
export interface IPlanterClient {
|
||||
export interface IPlanters {
|
||||
Name: string;
|
||||
Plants: IPlantClient[];
|
||||
Plants: IPlants[];
|
||||
}
|
||||
|
||||
export interface IPlanterDatabase {
|
||||
Name: string;
|
||||
Plants: IPlantDatabase[];
|
||||
export interface IGardening {
|
||||
Planters?: IPlanters[];
|
||||
}
|
||||
|
||||
export interface IGardeningClient {
|
||||
Planters: IPlanterClient[];
|
||||
}
|
||||
|
||||
export interface IGardeningDatabase {
|
||||
Planters: IPlanterDatabase[];
|
||||
}
|
||||
|
||||
export interface IApartmentClient {
|
||||
Gardening: IGardeningClient;
|
||||
export interface IApartment {
|
||||
Gardening: IGardening;
|
||||
Rooms: IRoom[];
|
||||
FavouriteLoadouts: IFavouriteLoadout[];
|
||||
}
|
||||
|
||||
export interface IApartmentDatabase {
|
||||
Gardening: IGardeningDatabase;
|
||||
Gardening: IGardening;
|
||||
Rooms: IRoom[];
|
||||
FavouriteLoadouts: IFavouriteLoadoutDatabase[];
|
||||
}
|
||||
|
@ -1,16 +1,18 @@
|
||||
import { IMongoDate, IOid } from "./commonTypes";
|
||||
|
||||
export interface IItemPrice {
|
||||
ItemType: string;
|
||||
ItemType: string | string[]; // If string[], preprocessing will use RNG to pick one for the current period.
|
||||
ItemCount: number;
|
||||
ProductCategory: string;
|
||||
}
|
||||
|
||||
export interface IItemPricePreprocessed extends Omit<IItemPrice, "ItemType"> {
|
||||
ItemType: string;
|
||||
}
|
||||
|
||||
export interface IItemManifest {
|
||||
StoreItem: string;
|
||||
ItemPrices?: IItemPrice[];
|
||||
RegularPrice?: number[];
|
||||
PremiumPrice?: number[];
|
||||
Bin: string;
|
||||
QuantityMultiplier: number;
|
||||
Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.
|
||||
@ -21,6 +23,10 @@ export interface IItemManifest {
|
||||
Id: IOid;
|
||||
}
|
||||
|
||||
export interface IItemManifestPreprocessed extends Omit<IItemManifest, "ItemPrices"> {
|
||||
ItemPrices?: IItemPricePreprocessed[];
|
||||
}
|
||||
|
||||
export interface IVendorInfo {
|
||||
_id: IOid;
|
||||
TypeName: string;
|
||||
@ -32,6 +38,14 @@ export interface IVendorInfo {
|
||||
Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing.
|
||||
}
|
||||
|
||||
export interface IVendorManifest {
|
||||
export interface IVendorInfoPreprocessed extends Omit<IVendorInfo, "ItemManifest"> {
|
||||
ItemManifest: IItemManifestPreprocessed[];
|
||||
}
|
||||
|
||||
export interface IRawVendorManifest {
|
||||
VendorInfo: IVendorInfo;
|
||||
}
|
||||
|
||||
export interface IVendorManifestPreprocessed {
|
||||
VendorInfo: IVendorInfoPreprocessed;
|
||||
}
|
||||
|
@ -10,9 +10,7 @@ export interface IWorldState {
|
||||
LiteSorties: ILiteSortie[];
|
||||
SyndicateMissions: ISyndicateMissionInfo[];
|
||||
GlobalUpgrades: IGlobalUpgrade[];
|
||||
ActiveMissions: IFissure[];
|
||||
NodeOverrides: INodeOverride[];
|
||||
PVPChallengeInstances: IPVPChallengeInstance[];
|
||||
EndlessXpChoices: IEndlessXpChoice[];
|
||||
SeasonInfo: {
|
||||
Activation: IMongoDate;
|
||||
@ -73,18 +71,6 @@ export interface IGlobalUpgrade {
|
||||
LocalizeDescTag: string;
|
||||
}
|
||||
|
||||
export interface IFissure {
|
||||
_id: IOid;
|
||||
Region: number;
|
||||
Seed: number;
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Node: string;
|
||||
MissionType: string;
|
||||
Modifier: string;
|
||||
Hard?: boolean;
|
||||
}
|
||||
|
||||
export interface INodeOverride {
|
||||
_id: IOid;
|
||||
Activation?: IMongoDate;
|
||||
@ -111,13 +97,6 @@ export interface ISortie {
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface ISortieMission {
|
||||
missionType: string;
|
||||
modifierType: string;
|
||||
node: string;
|
||||
tileset: string;
|
||||
}
|
||||
|
||||
export interface ILiteSortie {
|
||||
_id: IOid;
|
||||
Activation: IMongoDate;
|
||||
@ -131,21 +110,6 @@ export interface ILiteSortie {
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface IPVPChallengeInstance {
|
||||
_id: IOid;
|
||||
challengeTypeRefID: string;
|
||||
startDate: IMongoDate;
|
||||
endDate: IMongoDate;
|
||||
params: {
|
||||
n: string; // "ScriptParamValue";
|
||||
v: number;
|
||||
}[];
|
||||
isGenerated: boolean;
|
||||
PVPMode: string;
|
||||
subChallenges: IOid[];
|
||||
Category: string; // "PVPChallengeTypeCategory_WEEKLY" | "PVPChallengeTypeCategory_WEEKLY_ROOT" | "PVPChallengeTypeCategory_DAILY";
|
||||
}
|
||||
|
||||
export interface IEndlessXpChoice {
|
||||
Category: string;
|
||||
Choices: string[];
|
||||
@ -162,7 +126,7 @@ export interface ISeasonChallenge {
|
||||
export interface ICalendarSeason {
|
||||
Activation: IMongoDate;
|
||||
Expiry: IMongoDate;
|
||||
Season: "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL";
|
||||
Season: string; // "CST_UNDEFINED" | "CST_WINTER" | "CST_SPRING" | "CST_SUMMER" | "CST_FALL"
|
||||
Days: ICalendarDay[];
|
||||
YearIteration: number;
|
||||
Version: number;
|
||||
|
@ -1,38 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGMDCCBRigAwIBAgIQX4800cgswlDH/QexMSnnnjANBgkqhkiG9w0BAQsFADCB
|
||||
jzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQD
|
||||
Ey5TZWN0aWdvIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB
|
||||
MB4XDTI1MDMwNjAwMDAwMFoXDTI2MDMwNjIzNTk1OVowGDEWMBQGA1UEAwwNKi5m
|
||||
YWtldGxzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMe42XWK
|
||||
HJuR7doFTX79zrEKfTlD2hjRIif3dHKJNTJNvZa52mIoHelP7RVUuFOhp7aZCNLh
|
||||
IEzDyZObl8vwO6L2PVu5tbBEEoNixbpfhc8ZICEBuVo2UAhnJFcMJtuvtrCq+7ye
|
||||
oczM/k/nh8FBz2WnLzWs4CZt1sa5knZXFmBmsHJQtQIC6vx7QzVcKGOlAosIEHSK
|
||||
X4nIz5fLgWSzor1Gay56j31PTk+qRvlPQM2aKiLWnlLfRED4zHJqLe94itu8llPX
|
||||
b6g+cLxxRKUpMqtG/15cDdBZwv40Dja7bmNfe1u4w2QCVLjvHVaVpNXbcRay/Mhn
|
||||
M1w5LzDZmV58b18CAwEAAaOCAvwwggL4MB8GA1UdIwQYMBaAFI2MXsRUrYrhd+mb
|
||||
+ZsF4bgBjWHhMB0GA1UdDgQWBBS6/x/N38wMJrQq/cE1oIcRERMonTAOBgNVHQ8B
|
||||
MIIGLzCCBRegAwIBAgIRAILIyLcitteoEGcJt1QBXvcwDQYJKoZIhvcNAQELBQAw
|
||||
gY8xCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
|
||||
BgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDE3MDUGA1UE
|
||||
AxMuU2VjdGlnbyBSU0EgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBD
|
||||
QTAeFw0yNDA4MDIwMDAwMDBaFw0yNTA4MDIyMzU5NTlaMBcxFTATBgNVBAMMDCou
|
||||
dmlhdGxzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTToSjY
|
||||
3aUIxjghIkikJfFExVwSEzIM2XaQNJE+SxQ6Cc+xUR5QJrMJnM/39sH5c5imMEUo
|
||||
2OnstCIaVMPx5ZPN+HXLsvmoVAe2/xYe7emnZ5ZFTUXPyqkzDRg0hkMJiWWo/Nmf
|
||||
ypZfUJoz6hVkXwsgNFPTVuo7aECQFlZslh2HQVDOfBaNBxQBaOJ5vf6nllf/aLyB
|
||||
tZ74nlLynVYV9kYzISP4dUcxQ+D4HZgIxyOQfcN3EHUS1ZVaIp8hupOygF8zGQyJ
|
||||
uzFozzg5I59U+hT1yQG3FlwTBnP+sA0+hW0LBTbWSISm0If1SgHlUEqxLlosjuTG
|
||||
BG45h9o2bAz9po0CAwEAAaOCAvswggL3MB8GA1UdIwQYMBaAFI2MXsRUrYrhd+mb
|
||||
+ZsF4bgBjWHhMB0GA1UdDgQWBBQ/OeA2gLbVIKIuIitYRqSRUWMP3TAOBgNVHQ8B
|
||||
Af8EBAMCBaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
|
||||
BQUHAwIwSQYDVR0gBEIwQDA0BgsrBgEEAbIxAQICBzAlMCMGCCsGAQUFBwIBFhdo
|
||||
dHRwczovL3NlY3RpZ28uY29tL0NQUzAIBgZngQwBAgEwgYQGCCsGAQUFBwEBBHgw
|
||||
djBPBggrBgEFBQcwAoZDaHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUlNB
|
||||
RG9tYWluVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNydDAjBggrBgEFBQcwAYYX
|
||||
aHR0cDovL29jc3Auc2VjdGlnby5jb20wJQYDVR0RBB4wHIINKi5mYWtldGxzLmNv
|
||||
bYILZmFrZXRscy5jb20wggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB2AJaXZL9V
|
||||
WJet90OHaDcIQnfp8DrV9qTzNm5GpD8PyqnGAAABlWsz5fgAAAQDAEcwRQIgTN7Y
|
||||
/mDqiD3RbGVLEOQK2wvXsboBolBRwGJFuFEsDScCIQCQ0qfb/0V8qqSxrkx/PiVS
|
||||
1lSn5gBEnQUiQOkefcnW0gB2ABmG1Mcoqm/+ugNveCpNAZGqzi1yMQ+uzl1wQS0l
|
||||
TMfUAAABlWsz5dAAAAQDAEcwRQIhAJnQJyrSCWWdi9Kyoa7XuMGyDKt183jJMY0E
|
||||
71abTuBOAiBC+WnK1esG6xr8aVGHRcc+1U/I7LiaG3LCRMYtCKrTGwB2AMs49xWJ
|
||||
fIShRF9bwd37yW7ymlnNRwppBYWwyxTDFFjnAAABlWsz5f4AAAQDAEcwRQIhAJUs
|
||||
4PWDwyQJnCxCyEwFlFUY2uYQkGrQPA9f9Sw5Xk1fAiB63eQtZQGjvzvhOghy6z9a
|
||||
8oGYbDfDQ/zfisMYO7rM6zANBgkqhkiG9w0BAQsFAAOCAQEAEHnSoeBbWiK3CS3a
|
||||
px0BL+YXxRxdUcTMHgn5o+LlI9sWlpf+JLXmn7Z4QA6fAwT4k/Ue7xsmIq0OraDk
|
||||
/pEVXWm1HO/9wUkGQg0DBi77BpfHircd7OWIMdt250Q8UAmZkOyhVgnwBcScqMwq
|
||||
2T5CPaYvYGgYWx/qkIBv7JqhVbrP82rnF9b9ZUZ8GIE31chBmtMva9AsnAN5dmRw
|
||||
81bVvPWXUfX30CYu5sxeWL06Zpy9nfJumxZri1SWXNTBjSvud2jsZ8tSCUAWLL/4
|
||||
ui3Vien9m2oMOpaA8xbS88ZTk9Alm/o5febEKJZUPlytQzij8gQpiovFw2v+Cdei
|
||||
+tFXKw==
|
||||
aHR0cDovL29jc3Auc2VjdGlnby5jb20wIwYDVR0RBBwwGoIMKi52aWF0bHMuY29t
|
||||
ggp2aWF0bHMuY29tMIIBfwYKKwYBBAHWeQIEAgSCAW8EggFrAWkAdQDd3Mo0ldfh
|
||||
FgXnlTL6x5/4PRxQ39sAOhQSdgosrLvIKgAAAZEVLi9VAAAEAwBGMEQCIGiZNOV7
|
||||
IvcHKU7nEaxFgWPpUu2CxyULg1ueJTYwTT12AiAJWQv3RrqCtOJC7JEdztILs3Bn
|
||||
an9s0Bf93uOE4C/LiAB3AA3h8jAr0w3BQGISCepVLvxHdHyx1+kw7w5CHrR+Tqo0
|
||||
AAABkRUuLxAAAAQDAEgwRgIhAOhlC+IpJV3uAaDCRXi6RZ+V8++QaLaTEtqFp2UP
|
||||
yWeSAiEA8qtGDk1RE1VGwQQcJCf3CBYU5YTlsZNi7F6hEONLuzMAdwAS8U40vVNy
|
||||
TIQGGcOPP3oT+Oe1YoeInG0wBYTr5YYmOgAAAZEVLi7kAAAEAwBIMEYCIQDWCnSm
|
||||
N+2/xxo8bl3pbpNBEBZZIwnYPQW0A+SJ0+dboQIhANjH2L0xV/+rPuPMzK40vk3J
|
||||
1fWHLocLjpgaxGhsBAOzMA0GCSqGSIb3DQEBCwUAA4IBAQBcObVjc1zFdOER50ZF
|
||||
mI+WyVF8t6nV6dm3zIDraLA4++zKUu9UKNJm9YPqLdPP7uTLHz6wuBNmyuWPdF0r
|
||||
qAf4vsK3tcAds7kjK8injewEUCPG20mtNMUHyhlNEOJR2ySPPQ6Q+t+TtGAnimKa
|
||||
Zr86quYgYaJYhoEEXcbB9fMoDQYlJDzgT2DXvfM4cyoden2tYZ3gQS6ftiXacBe0
|
||||
WzFWYZ8mIP2Kb+D9tCapB9MVUzu3XJVy3S2FLQEWcWIvjnpad73a0/35i/nro6/k
|
||||
TSK+MKBEBaNZuHJ8ubCToo1BftnsS8HuEPTNe8W1hyc2YmT9f5YQP6HWB2rxjH42
|
||||
OTXh
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB
|
||||
|
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