merge upstream

This commit is contained in:
Vitruvio 2025-06-24 10:44:35 -07:00
commit 1655d7da1e
109 changed files with 3356 additions and 6869 deletions

View File

@ -1,6 +1,7 @@
name: Build
on:
push: {}
push:
branches: ["main"]
pull_request: {}
jobs:
build:

View File

@ -4,9 +4,9 @@ on:
branches:
- main
jobs:
docker:
docker-amd64:
if: github.repository == 'OpenWF/SpaceNinjaServer'
runs-on: ubuntu-latest
runs-on: amd64
steps:
- name: Set up Docker buildx
uses: docker/setup-buildx-action@v3
@ -18,8 +18,27 @@ jobs:
- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
platforms: linux/amd64
push: true
tags: |
openwf/spaceninjaserver:latest
openwf/spaceninjaserver:${{ github.sha }}
docker-arm64:
if: github.repository == 'OpenWF/SpaceNinjaServer'
runs-on: arm64
steps:
- name: Set up Docker buildx
uses: docker/setup-buildx-action@v3
- name: Log in to container registry
uses: docker/login-action@v3
with:
username: openwf
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
platforms: linux/arm64
push: true
tags: |
openwf/spaceninjaserver:latest-arm64
openwf/spaceninjaserver:${{ github.sha }}-arm64

17
AGENTS.md Normal file
View File

@ -0,0 +1,17 @@
## In General
### Prerequisites
Use `npm i` or `npm ci` to install all dependencies.
### Testing
Use `npm run verify` to verify that your changes pass TypeScript's checks.
### Formatting
Use `npm run prettier` to ensure your formatting matches the expected format. Failing to do so will cause CI failure.
## WebUI Specific
The translation system is designed around additions being made to `static/webui/translations/en.js`. They are copied over for translation via `npm run update-translations`. DO NOT produce non-English strings; we want them to be translated by humans who can understand the full context.

View File

@ -1,53 +1,11 @@
FROM node:18-alpine3.19
FROM node:24-alpine3.21
ENV APP_MONGODB_URL=mongodb://mongodb:27017/openWF
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_CLAIMING_BLUEPRINT_REFUNDS_INGREDIENTS=false
ENV APP_DONT_SUBTRACT_VOIDTRACES=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_SYNDICATE_MISSIONS_REPEATABLE=false
ENV APP_INSTANT_FINISH_RIVEN_CHALLENGE=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_SPOOF_MASTERY_RANK=-1
RUN apk add --no-cache bash sed wget jq
RUN apk add --no-cache bash jq
COPY . /app
WORKDIR /app
RUN npm i --omit=dev
RUN npm run build
ENTRYPOINT ["/app/docker-entrypoint.sh"]

View File

@ -16,6 +16,7 @@ SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [confi
- `myIrcAddresses` can be used to point to an IRC server. If not provided, defaults to `[ myAddress ]`.
- `worldState.eidolonOverride` can be set to `day` or `night` to lock the time to day/fass and night/vome on Plains of Eidolon/Cambion Drift.
- `worldState.vallisOverride` can be set to `warm` or `cold` to lock the temperature on Orb Vallis.
- `worldState.duviriOverride` can be set to `joy`, `anger`, `envy`, `sorrow`, or `fear` to lock the Duviri spiral.
- `worldState.nightwaveOverride` will lock the nightwave season, assuming the client is new enough for it. Valid values:
- `RadioLegionIntermission13Syndicate` for Nora's Mix Vol. 9
- `RadioLegionIntermission12Syndicate` for Nora's Mix Vol. 8
@ -33,3 +34,4 @@ SpaceNinjaServer requires a `config.json`. To set it up, you can copy the [confi
- `RadioLegion2Syndicate` for The Emissary
- `RadioLegionIntermissionSyndicate` for Intermission I
- `RadioLegionSyndicate` for The Wolf of Saturn Six
- `worldState.circuitGameModes` can be provided with an array of valid game modes (`Survival`, `VoidFlood`, `Excavation`, `Defense`, `Exterminate`, `Assassination`, `Alchemy`)

View File

@ -13,13 +13,16 @@
"skipTutorial": false,
"skipAllDialogue": false,
"unlockAllScans": false,
"unlockAllMissions": false,
"infiniteCredits": false,
"infinitePlatinum": false,
"infiniteEndo": false,
"infiniteRegalAya": false,
"infiniteHelminthMaterials": false,
"claimingBlueprintRefundsIngredients": false,
"dontSubtractPurchaseCreditCost": false,
"dontSubtractPurchasePlatinumCost": false,
"dontSubtractPurchaseItemCost": false,
"dontSubtractPurchaseStandingCost": false,
"dontSubtractVoidTraces": false,
"dontSubtractConsumables": false,
"unlockAllShipFeatures": false,
@ -35,10 +38,12 @@
"noDailyFocusLimit": false,
"noArgonCrystalDecay": false,
"noMasteryRankUpCooldown": false,
"noVendorPurchaseLimits": true,
"noVendorPurchaseLimits": false,
"noDeathMarks": false,
"noKimCooldowns": false,
"fullyStockedVendors": false,
"syndicateMissionsRepeatable": false,
"unlockAllProfitTakerStages": false,
"instantFinishRivenChallenge": false,
"instantResourceExtractorDrones": false,
"noResourceExtractorDronesDamage": false,
@ -49,7 +54,14 @@
"noDojoResearchCosts": false,
"noDojoResearchTime": false,
"fastClanAscension": false,
"missionsCanGiveAllRelics": false,
"unlockAllSimarisResearchEntries": false,
"spoofMasteryRank": -1,
"nightwaveStandingMultiplier": 1,
"unfaithfulBugFixes": {
"ignore1999LastRegionPlayed": false,
"fixXtraCheeseTimer": false
},
"worldState": {
"creditBoost": false,
"affinityBoost": false,
@ -57,6 +69,11 @@
"starDays": true,
"eidolonOverride": "",
"vallisOverride": "",
"nightwaveOverride": ""
"duviriOverride": "",
"nightwaveOverride": "",
"circuitGameModes": null
},
"dev": {
"keepVendorsExpired": false
}
}

View File

@ -1,62 +1,20 @@
services:
spaceninjaserver:
# build: .
# The image to use. If you have an ARM CPU, replace 'latest' with 'latest-arm64'.
image: openwf/spaceninjaserver:latest
environment:
APP_MONGODB_URL: mongodb://openwfagent:spaceninjaserver@mongodb:27017/
# Following environment variables are set to default image values.
# Uncomment to edit.
# APP_MY_ADDRESS: localhost
# 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_CLAIMING_BLUEPRINT_REFUNDS_INGREDIENTS: false
# APP_DONT_SUBTRACT_VOIDTRACES: 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_SYNDICATE_MISSIONS_REPEATABLE: false
# APP_INSTANT_FINISH_RIVEN_CHALLENGE: 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_SPOOF_MASTERY_RANK: -1
volumes:
- ./docker-data/static:/app/static/data
- ./docker-data/conf:/app/conf
- ./docker-data/static-data:/app/static/data
- ./docker-data/logs:/app/logs
ports:
- 80:80
- 443:443
# Normally, the image is fetched from Docker Hub, but you can use the local Dockerfile by removing "image" above and adding this:
#build: .
# Works best when using `docker-compose up --force-recreate --build`.
depends_on:
- mongodb
mongodb:
@ -66,3 +24,4 @@ services:
MONGO_INITDB_ROOT_PASSWORD: spaceninjaserver
volumes:
- ./docker-data/database:/data/db
command: mongod --quiet --logpath /dev/null

View File

@ -1,24 +1,8 @@
#!/bin/bash
set -e
# Set up the configuration file using environment variables.
echo '{
"logger": {
"files": true,
"level": "trace",
"__valid_levels": "fatal, error, warn, info, http, debug, trace"
}
}
' > config.json
if [ ! -f conf/config.json ]; then
jq --arg value "mongodb://openwfagent:spaceninjaserver@mongodb:27017/" '.mongodbUrl = $value' /app/config.json.example > /app/conf/config.json
fi
for config in $(env | grep "APP_")
do
var=$(echo "${config}" | tr '[:upper:]' '[:lower:]' | sed 's/app_//g' | sed -E 's/_([a-z])/\U\1/g' | sed 's/=.*//g')
val=$(echo "${config}" | sed 's/.*=//g')
jq --arg variable "$var" --arg value "$val" '.[$variable] += try [$value|fromjson][] catch $value' config.json > config.tmp
mv config.tmp config.json
done
npm i --omit=dev
npm run build
exec npm run start
exec npm run start -- --configPath conf/config.json

657
package-lock.json generated
View File

@ -11,6 +11,8 @@
"dependencies": {
"@types/express": "^5",
"@types/morgan": "^1.9.9",
"@types/websocket": "^1.0.10",
"@types/ws": "^8.18.1",
"crc-32": "^1.2.2",
"express": "^5",
"json-with-bigint": "^3.4.4",
@ -18,20 +20,22 @@
"morgan": "^1.10.0",
"ncp": "^2.0.0",
"typescript": "^5.5",
"warframe-public-export-plus": "^0.5.66",
"undici": "^7.10.0",
"warframe-public-export-plus": "^0.5.68",
"warframe-riven-info": "^0.1.2",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0"
"winston-daily-rotate-file": "^5.0.0",
"ws": "^8.18.2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"@typescript/native-preview": "^7.0.0-dev.20250523.1",
"chokidar": "^4.0.3",
"eslint": "^8",
"eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3",
"ts-node-dev": "^2.0.0",
"tsconfig-paths": "^4.2.0"
"tree-kill": "^1.2.2"
}
},
"node_modules/@colors/colors": {
@ -43,19 +47,6 @@
"node": ">=0.1.90"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@dabh/diagnostics": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
@ -121,9 +112,9 @@
}
},
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -171,9 +162,9 @@
}
},
"node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -216,34 +207,6 @@
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@mongodb-js/saslprep": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz",
@ -304,34 +267,6 @@
"url": "https://opencollective.com/pkgr"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
@ -437,20 +372,6 @@
"@types/send": "*"
}
},
"node_modules/@types/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/strip-json-comments": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz",
"integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/triple-beam": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
@ -463,6 +384,15 @@
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
"license": "MIT"
},
"node_modules/@types/websocket": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.10.tgz",
"integrity": "sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/whatwg-url": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
@ -472,6 +402,15 @@
"@types/webidl-conversions": "*"
}
},
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"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",
@ -849,19 +788,6 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/acorn-walk": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "^8.11.0"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -905,27 +831,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true,
"license": "MIT"
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -964,19 +869,6 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
@ -998,9 +890,9 @@
}
},
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1029,12 +921,20 @@
"node": ">=16.20.1"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
"node_modules/bufferutil": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
"integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/bytes": {
"version": "3.1.2",
@ -1102,41 +1002,19 @@
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 8.10.0"
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chokidar/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/color": {
@ -1261,13 +1139,6 @@
"node": ">=0.8"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@ -1316,16 +1187,6 @@
"node": ">= 0.8"
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@ -1353,16 +1214,6 @@
"node": ">= 0.4"
}
},
"node_modules/dynamic-dedupe": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
"integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"xtend": "^4.0.0"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -1552,9 +1403,9 @@
}
},
"node_modules/eslint/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1886,21 +1737,6 @@
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -1983,9 +1819,9 @@
}
},
"node_modules/glob/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -2173,35 +2009,6 @@
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"license": "MIT"
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -2310,19 +2117,6 @@
"integrity": "sha512-AhpYAAaZsPjU7smaBomDt1SOQshi9rEm6BlTbfVwsG1vNmeHKtEedJi62sHZzJTyKNtwzmNnrsd55kjwJ7054A==",
"license": "MIT"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/kareem": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
@ -2402,13 +2196,6 @@
"node": ">= 12.0.0"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true,
"license": "ISC"
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -2506,29 +2293,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"license": "MIT",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
@ -2711,14 +2475,17 @@
"node": ">= 0.6"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"node_modules/node-gyp-build": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
"optional": true,
"peer": true,
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/object-hash": {
@ -2883,13 +2650,6 @@
"node": ">=8"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/path-to-regexp": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
@ -3048,37 +2808,17 @@
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
"node": ">= 14.18.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/resolve-from": {
@ -3360,27 +3100,6 @@
"is-arrayish": "^0.3.1"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/sparse-bitfield": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
@ -3430,16 +3149,6 @@
"node": ">=8"
}
},
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@ -3466,19 +3175,6 @@
"node": ">=8"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/synckit": {
"version": "0.11.4",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz",
@ -3575,137 +3271,6 @@
"typescript": ">=4.8.4"
}
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/ts-node-dev": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz",
"integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^3.5.1",
"dynamic-dedupe": "^0.3.0",
"minimist": "^1.2.6",
"mkdirp": "^1.0.4",
"resolve": "^1.0.0",
"rimraf": "^2.6.1",
"source-map-support": "^0.5.12",
"tree-kill": "^1.2.2",
"ts-node": "^10.4.0",
"tsconfig": "^7.0.0"
},
"bin": {
"ts-node-dev": "lib/bin.js",
"tsnd": "lib/bin.js"
},
"engines": {
"node": ">=0.8.0"
},
"peerDependencies": {
"node-notifier": "*",
"typescript": "*"
},
"peerDependenciesMeta": {
"node-notifier": {
"optional": true
}
}
},
"node_modules/ts-node-dev/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/tsconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
"integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/strip-bom": "^3.0.0",
"@types/strip-json-comments": "0.0.30",
"strip-bom": "^3.0.0",
"strip-json-comments": "^2.0.0"
}
},
"node_modules/tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tsconfig/node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@ -3766,6 +3331,15 @@
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz",
"integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
@ -3791,19 +3365,27 @@
"punycode": "^2.1.0"
}
},
"node_modules/utf-8-validate": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"node-gyp-build": "^4.3.0"
},
"engines": {
"node": ">=6.14.2"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"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",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true,
"license": "MIT"
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -3814,9 +3396,9 @@
}
},
"node_modules/warframe-public-export-plus": {
"version": "0.5.66",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.66.tgz",
"integrity": "sha512-AU7XQA96OfYrLm2RioCwDjjdI3IrsmUiqebXyE+bpM0iST+4x/NHu8LTRT4Oygfo/2OBtDYhib7G6re0EeAe5g=="
"version": "0.5.68",
"resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.68.tgz",
"integrity": "sha512-KMmwCVeQ4k+EN73UZqxnM+qQdPsST8geWoJCP7US5LT6JcRxa8ptmqYXwCzaLtckBLZyVbamsxKZAxPPJckxsA=="
},
"node_modules/warframe-riven-info": {
"version": "0.1.2",
@ -3931,24 +3513,25 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true,
"node_modules/ws": {
"version": "8.18.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
"license": "MIT",
"engines": {
"node": ">=0.4"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yocto-queue": {

View File

@ -5,9 +5,14 @@
"main": "index.ts",
"scripts": {
"start": "node --enable-source-maps --import ./build/src/pathman.js build/src/index.js",
"dev": "ts-node-dev --openssl-legacy-provider -r tsconfig-paths/register src/index.ts ",
"build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
"build:dev": "tsc --incremental --sourceMap",
"build-and-start": "npm run build && npm run start",
"build-and-start:bun": "npm run verify && npm run bun-run",
"dev": "node scripts/dev.js",
"dev:bun": "bun scripts/dev.js",
"verify": "tsgo --noEmit",
"bun-run": "bun src/index.ts",
"lint": "eslint --ext .ts .",
"lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
"lint:fix": "eslint --fix --ext .ts .",
@ -18,6 +23,8 @@
"dependencies": {
"@types/express": "^5",
"@types/morgan": "^1.9.9",
"@types/websocket": "^1.0.10",
"@types/ws": "^8.18.1",
"crc-32": "^1.2.2",
"express": "^5",
"json-with-bigint": "^3.4.4",
@ -25,19 +32,21 @@
"morgan": "^1.10.0",
"ncp": "^2.0.0",
"typescript": "^5.5",
"warframe-public-export-plus": "^0.5.66",
"undici": "^7.10.0",
"warframe-public-export-plus": "^0.5.68",
"warframe-riven-info": "^0.1.2",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0"
"winston-daily-rotate-file": "^5.0.0",
"ws": "^8.18.2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"@typescript/native-preview": "^7.0.0-dev.20250523.1",
"chokidar": "^4.0.3",
"eslint": "^8",
"eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3",
"ts-node-dev": "^2.0.0",
"tsconfig-paths": "^4.2.0"
"tree-kill": "^1.2.2"
}
}

58
scripts/dev.js Normal file
View File

@ -0,0 +1,58 @@
/* eslint-disable */
const { spawn } = require("child_process");
const chokidar = require("chokidar");
const kill = require("tree-kill");
let secret = "";
for (let i = 0; i != 10; ++i) {
secret += String.fromCharCode(Math.floor(Math.random() * 26) + 0x41);
}
const args = [...process.argv].splice(2);
args.push("--dev");
args.push("--secret");
args.push(secret);
let buildproc, runproc;
const spawnopts = { stdio: "inherit", shell: true };
function run(changedFile) {
if (changedFile) {
console.log(`Change to ${changedFile} detected`);
}
if (buildproc) {
kill(buildproc.pid);
buildproc = undefined;
}
if (runproc) {
kill(runproc.pid);
runproc = undefined;
}
const thisbuildproc = spawn("npm", ["run", process.versions.bun ? "verify" : "build:dev"], spawnopts);
const thisbuildstart = Date.now();
buildproc = thisbuildproc;
buildproc.on("exit", code => {
if (buildproc !== thisbuildproc) {
return;
}
buildproc = undefined;
if (code === 0) {
console.log(`${process.versions.bun ? "Verified" : "Built"} in ${Date.now() - thisbuildstart} ms`);
runproc = spawn("npm", ["run", process.versions.bun ? "bun-run" : "start", "--", ...args], spawnopts);
runproc.on("exit", () => {
runproc = undefined;
});
}
});
}
run();
chokidar.watch("src").on("change", run);
chokidar.watch("static/fixed_responses").on("change", run);
chokidar.watch("static/webui").on("change", async () => {
try {
await fetch("http://localhost/custom/webuiFileChangeDetected?secret=" + secret);
} catch (e) {}
});

View File

@ -1,6 +1,7 @@
// Based on https://onlyg.it/OpenWF/Translations/src/branch/main/update.php
// Converted via ChatGPT-4o
/* eslint-disable */
const fs = require("fs");
function extractStrings(content) {

View File

@ -1,4 +1,4 @@
import { getCalendarProgress, getInventory } from "@/src/services/inventoryService";
import { checkCalendarChallengeCompletion, getCalendarProgress, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { getWorldState } from "@/src/services/worldStateService";
@ -12,27 +12,23 @@ export const completeCalendarEventController: RequestHandler = async (req, res)
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;
const dayIndex = calendarProgress.SeasonProgress.LastCompletedDayIdx + 1;
const day = currentSeason.Days[dayIndex];
if (day.events.length != 0) {
if (day.events[0].type == "CET_CHALLENGE") {
throw new Error(`completeCalendarEvent should not be used for challenges`);
}
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}`);
}
}
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
calendarProgress.SeasonProgress.LastCompletedDayIdx = dayIndex;
checkCalendarChallengeCompletion(calendarProgress, currentSeason);
await inventory.save();
res.json({
InventoryChanges: inventoryChanges,

View File

@ -0,0 +1,107 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMiscItems, freeUpSlot, getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IOid } from "@/src/types/commonTypes";
import { ICrewShipComponentFingerprint, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
import { ExportCustoms, ExportDojoRecipes } from "warframe-public-export-plus";
export const crewShipFusionController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const payload = getJSONfromString<ICrewShipFusionRequest>(String(req.body));
const isWeapon = inventory.CrewShipWeapons.id(payload.PartA.$oid);
const itemA = isWeapon ?? inventory.CrewShipWeaponSkins.id(payload.PartA.$oid)!;
const category = isWeapon ? "CrewShipWeapons" : "CrewShipWeaponSkins";
const salvageCategory = isWeapon ? "CrewShipSalvagedWeapons" : "CrewShipSalvagedWeaponSkins";
const itemB = inventory[payload.SourceRecipe ? salvageCategory : category].id(payload.PartB.$oid)!;
const tierA = itemA.ItemType.charCodeAt(itemA.ItemType.length - 1) - 65;
const tierB = itemB.ItemType.charCodeAt(itemB.ItemType.length - 1) - 65;
const inventoryChanges: IInventoryChanges = {};
// Charge partial repair cost if fusing with an identified but unrepaired part
if (payload.SourceRecipe) {
const recipe = ExportDojoRecipes.research[payload.SourceRecipe];
updateCurrency(inventory, Math.round(recipe.price * 0.4), false, inventoryChanges);
const miscItemChanges = recipe.ingredients.map(x => ({ ...x, ItemCount: Math.round(x.ItemCount * -0.4) }));
addMiscItems(inventory, miscItemChanges);
inventoryChanges.MiscItems = miscItemChanges;
}
// Remove inferior item
if (payload.SourceRecipe) {
inventory[salvageCategory].pull({ _id: payload.PartB.$oid });
inventoryChanges.RemovedIdItems = [{ ItemId: payload.PartB }];
} else {
const inferiorId = tierA < tierB ? payload.PartA : payload.PartB;
inventory[category].pull({ _id: inferiorId.$oid });
inventoryChanges.RemovedIdItems = [{ ItemId: inferiorId }];
freeUpSlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS);
inventoryChanges[InventorySlot.RJ_COMPONENT_AND_ARMAMENTS] = { count: -1, platinum: 0, Slots: 1 };
}
// Upgrade superior item
const superiorItem = tierA < tierB ? itemB : itemA;
const inferiorItem = tierA < tierB ? itemA : itemB;
const fingerprint: ICrewShipComponentFingerprint = JSON.parse(
superiorItem.UpgradeFingerprint!
) as ICrewShipComponentFingerprint;
const inferiorFingerprint: ICrewShipComponentFingerprint = inferiorItem.UpgradeFingerprint
? (JSON.parse(inferiorItem.UpgradeFingerprint) as ICrewShipComponentFingerprint)
: { compat: "", buffs: [] };
if (isWeapon) {
for (let i = 0; i != fingerprint.buffs.length; ++i) {
const buffA = fingerprint.buffs[i];
const buffB = i < inferiorFingerprint.buffs.length ? inferiorFingerprint.buffs[i] : undefined;
const fvalA = buffA.Value / 0x3fffffff;
const fvalB = (buffB?.Value ?? 0) / 0x3fffffff;
const percA = 0.3 + fvalA * (0.6 - 0.3);
const percB = 0.3 + fvalB * (0.6 - 0.3);
const newPerc = Math.min(0.6, Math.max(percA, percB) * FUSE_MULTIPLIERS[Math.abs(tierA - tierB)]);
const newFval = (newPerc - 0.3) / (0.6 - 0.3);
buffA.Value = Math.trunc(newFval * 0x3fffffff);
}
} else {
const superiorMeta = ExportCustoms[superiorItem.ItemType].randomisedUpgrades ?? [];
const inferiorMeta = ExportCustoms[inferiorItem.ItemType].randomisedUpgrades ?? [];
for (let i = 0; i != inferiorFingerprint.buffs.length; ++i) {
const buffA = fingerprint.buffs[i];
const buffB = inferiorFingerprint.buffs[i];
const fvalA = buffA.Value / 0x3fffffff;
const fvalB = buffB.Value / 0x3fffffff;
const rangeA = superiorMeta[i].range;
const rangeB = inferiorMeta[i].range;
const percA = rangeA[0] + fvalA * (rangeA[1] - rangeA[0]);
const percB = rangeB[0] + fvalB * (rangeB[1] - rangeB[0]);
const newPerc = Math.min(rangeA[1], Math.max(percA, percB) * FUSE_MULTIPLIERS[Math.abs(tierA - tierB)]);
const newFval = (newPerc - rangeA[0]) / (rangeA[1] - rangeA[0]);
buffA.Value = Math.trunc(newFval * 0x3fffffff);
}
if (inferiorFingerprint.SubroutineIndex) {
const useSuperiorSubroutine = tierA < tierB ? !payload.UseSubroutineA : payload.UseSubroutineA;
if (!useSuperiorSubroutine) {
fingerprint.SubroutineIndex = inferiorFingerprint.SubroutineIndex;
}
}
}
superiorItem.UpgradeFingerprint = JSON.stringify(fingerprint);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
inventoryChanges[category] = [superiorItem.toJSON() as any];
await inventory.save();
res.json({
InventoryChanges: inventoryChanges
});
};
interface ICrewShipFusionRequest {
PartA: IOid;
PartB: IOid;
SourceRecipe: string;
UseSubroutineA: boolean;
}
const FUSE_MULTIPLIERS = [1.1, 1.05, 1.02];

View File

@ -30,15 +30,14 @@ export const fishmongerController: RequestHandler = async (req, res) => {
miscItemChanges.push({ ItemType: fish.ItemType, ItemCount: fish.ItemCount * -1 });
}
addMiscItems(inventory, miscItemChanges);
let affiliationMod;
if (gainedStanding && syndicateTag) affiliationMod = addStanding(inventory, syndicateTag, gainedStanding);
if (gainedStanding && syndicateTag) addStanding(inventory, syndicateTag, gainedStanding);
await inventory.save();
res.json({
InventoryChanges: {
MiscItems: miscItemChanges
},
SyndicateTag: syndicateTag,
StandingChange: affiliationMod?.Standing || 0
StandingChange: gainedStanding
});
};

View File

@ -43,7 +43,7 @@ export const focusController: RequestHandler = async (req, res) => {
inventory.FocusAbility ??= focusType;
inventory.FocusUpgrades.push({ ItemType: focusType });
if (inventory.FocusXP) {
inventory.FocusXP[focusPolarity] -= cost;
inventory.FocusXP[focusPolarity]! -= cost;
}
await inventory.save();
res.json({
@ -78,7 +78,7 @@ export const focusController: RequestHandler = async (req, res) => {
cost += ExportFocusUpgrades[focusType].baseFocusPointCost;
inventory.FocusUpgrades.push({ ItemType: focusType, Level: 0 });
}
inventory.FocusXP![focusPolarity] -= cost;
inventory.FocusXP![focusPolarity]! -= cost;
await inventory.save();
res.json({
FocusTypes: request.FocusTypes,
@ -96,7 +96,7 @@ export const focusController: RequestHandler = async (req, res) => {
const focusUpgradeDb = inventory.FocusUpgrades.find(entry => entry.ItemType == focusUpgrade.ItemType)!;
focusUpgradeDb.Level = focusUpgrade.Level;
}
inventory.FocusXP![focusPolarity] -= cost;
inventory.FocusXP![focusPolarity]! -= cost;
await inventory.save();
res.json({
FocusInfos: request.FocusInfos,
@ -123,7 +123,7 @@ export const focusController: RequestHandler = async (req, res) => {
const request = JSON.parse(String(req.body)) as IUnbindUpgradeRequest;
const focusPolarity = focusTypeToPolarity(request.FocusTypes[0]);
const inventory = await getInventory(accountId);
inventory.FocusXP![focusPolarity] -= 750_000 * request.FocusTypes.length;
inventory.FocusXP![focusPolarity]! -= 750_000 * request.FocusTypes.length;
addMiscItems(inventory, [
{
ItemType: "/Lotus/Types/Gameplay/Eidolon/Resources/SentientShards/SentientShardBrilliantItem",
@ -168,8 +168,10 @@ export const focusController: RequestHandler = async (req, res) => {
shard.ItemCount *= -1;
}
const inventory = await getInventory(accountId);
inventory.FocusXP ??= { AP_POWER: 0, AP_TACTIC: 0, AP_DEFENSE: 0, AP_ATTACK: 0, AP_WARD: 0 };
inventory.FocusXP[request.Polarity] += xp;
const polarity = request.Polarity;
inventory.FocusXP ??= {};
inventory.FocusXP[polarity] ??= 0;
inventory.FocusXP[polarity] += xp;
addMiscItems(inventory, request.Shards);
await inventory.save();
break;

View File

@ -2,6 +2,7 @@ import { RequestHandler } from "express";
import { applyStandingToVendorManifest, getVendorManifestByTypeName } from "@/src/services/serversideVendorsService";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { config } from "@/src/services/configService";
export const getVendorInfoController: RequestHandler = async (req, res) => {
let manifest = getVendorManifestByTypeName(req.query.vendor as string);
@ -14,6 +15,14 @@ export const getVendorInfoController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
manifest = applyStandingToVendorManifest(inventory, manifest);
if (config.dev?.keepVendorsExpired) {
manifest = {
VendorInfo: {
...manifest.VendorInfo,
Expiry: { $date: { $numberLong: "0" } }
}
};
}
}
res.json(manifest);

View File

@ -11,13 +11,13 @@ import {
import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { IOid } from "@/src/types/commonTypes";
import { IInventoryChanges, IPurchaseParams } from "@/src/types/purchaseTypes";
import { IInventoryChanges, IPurchaseParams, PurchaseSource } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
import { ExportBundles, ExportFlavour } from "warframe-public-export-plus";
export const giftingController: RequestHandler = async (req, res) => {
const data = getJSONfromString<IGiftingRequest>(String(req.body));
if (data.PurchaseParams.Source != 0 || !data.PurchaseParams.UsePremium) {
if (data.PurchaseParams.Source != PurchaseSource.Market || !data.PurchaseParams.UsePremium) {
throw new Error(`unexpected purchase params in gifting request: ${String(req.body)}`);
}

View File

@ -11,7 +11,7 @@ import {
scaleRequiredCount,
setGuildTechLogState
} from "@/src/services/guildService";
import { ExportDojoRecipes } from "warframe-public-export-plus";
import { ExportDojoRecipes, ExportRailjackWeapons } from "warframe-public-export-plus";
import { getAccountIdForRequest } from "@/src/services/loginService";
import {
addCrewShipWeaponSkin,
@ -442,6 +442,7 @@ const finishComponentRepair = (
...(category == "CrewShipWeaponSkins"
? addCrewShipWeaponSkin(inventory, salvageItem.ItemType, salvageItem.UpgradeFingerprint)
: addEquipment(inventory, category, salvageItem.ItemType, {
UpgradeType: ExportRailjackWeapons[salvageItem.ItemType].defaultUpgrades?.[0].ItemType,
UpgradeFingerprint: salvageItem.UpgradeFingerprint
})),
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, false)

View File

@ -30,8 +30,9 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
const request = getJSONfromString<IShardInstallRequest>(String(req.body));
const inventory = await getInventory(account._id.toString());
const suit = inventory.Suits.id(request.SuitId.$oid)!;
if (!suit.ArchonCrystalUpgrades || suit.ArchonCrystalUpgrades.length != 5) {
suit.ArchonCrystalUpgrades = [{}, {}, {}, {}, {}];
suit.ArchonCrystalUpgrades ??= [];
while (suit.ArchonCrystalUpgrades.length < request.Slot) {
suit.ArchonCrystalUpgrades.push({});
}
suit.ArchonCrystalUpgrades[request.Slot] = {
UpgradeType: request.UpgradeType,
@ -92,7 +93,8 @@ export const infestedFoundryController: RequestHandler = async (req, res) => {
}
// remove from suit
suit.ArchonCrystalUpgrades![request.Slot] = {};
suit.ArchonCrystalUpgrades![request.Slot].UpgradeType = undefined;
suit.ArchonCrystalUpgrades![request.Slot].Color = undefined;
await inventory.save();

View File

@ -6,13 +6,7 @@ import allDialogue from "@/static/fixed_responses/allDialogue.json";
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
import { IInventoryClient, IShipInventory, equipmentKeys } from "@/src/types/inventoryTypes/inventoryTypes";
import { IPolarity, ArtifactPolarity, EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
import {
ExportCustoms,
ExportFlavour,
ExportRegions,
ExportResources,
ExportVirtuals
} from "warframe-public-export-plus";
import { ExportCustoms, ExportFlavour, ExportResources, ExportVirtuals } from "warframe-public-export-plus";
import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService";
import {
addMiscItems,
@ -22,7 +16,7 @@ import {
generateRewardSeed
} from "@/src/services/inventoryService";
import { logger } from "@/src/utils/logger";
import { catBreadHash } from "@/src/helpers/stringHelpers";
import { addString, catBreadHash } from "@/src/helpers/stringHelpers";
import { Types } from "mongoose";
import { getNemesisManifest } from "@/src/helpers/nemesisHelpers";
import { getPersonalRooms } from "@/src/services/personalRoomsService";
@ -167,18 +161,6 @@ export const getInventoryResponse = async (
}
}
if (config.unlockAllMissions) {
inventoryResponse.Missions = [];
for (const tag of Object.keys(ExportRegions)) {
inventoryResponse.Missions.push({
Completes: 1,
Tier: 1,
Tag: tag
});
}
addString(inventoryResponse.NodeIntrosCompleted, "TeshinHardModeUnlocked");
}
if (config.unlockAllShipDecorations) {
inventoryResponse.ShipDecorations = [];
for (const [uniqueName, item] of Object.entries(ExportResources)) {
@ -339,14 +321,41 @@ export const getInventoryResponse = async (
}
}
if (config.unlockAllProfitTakerStages) {
inventoryResponse.CompletedJobChains ??= [];
const EudicoHeists = inventoryResponse.CompletedJobChains.find(x => x.LocationTag == "EudicoHeists");
if (EudicoHeists) {
EudicoHeists.Jobs = allEudicoHeistJobs;
} else {
inventoryResponse.CompletedJobChains.push({
LocationTag: "EudicoHeists",
Jobs: allEudicoHeistJobs
});
}
}
if (config.unlockAllSimarisResearchEntries) {
inventoryResponse.LibraryPersonalTarget = undefined;
inventoryResponse.LibraryPersonalProgress = [
"/Lotus/Types/Game/Library/Targets/Research1Target",
"/Lotus/Types/Game/Library/Targets/Research2Target",
"/Lotus/Types/Game/Library/Targets/Research3Target",
"/Lotus/Types/Game/Library/Targets/Research4Target",
"/Lotus/Types/Game/Library/Targets/Research5Target",
"/Lotus/Types/Game/Library/Targets/Research6Target",
"/Lotus/Types/Game/Library/Targets/Research7Target"
].map(type => ({ TargetType: type, Scans: 10, Completed: true }));
}
return inventoryResponse;
};
const addString = (arr: string[], str: string): void => {
if (arr.indexOf(str) == -1) {
arr.push(str);
}
};
const allEudicoHeistJobs = [
"/Lotus/Types/Gameplay/Venus/Jobs/Heists/HeistProfitTakerBountyOne",
"/Lotus/Types/Gameplay/Venus/Jobs/Heists/HeistProfitTakerBountyTwo",
"/Lotus/Types/Gameplay/Venus/Jobs/Heists/HeistProfitTakerBountyThree",
"/Lotus/Types/Gameplay/Venus/Jobs/Heists/HeistProfitTakerBountyFour"
];
const getExpRequiredForMr = (rank: number): number => {
if (rank <= 30) {

View File

@ -4,16 +4,16 @@ import { config } from "@/src/services/configService";
import { buildConfig } from "@/src/services/buildConfigService";
import { Account } from "@/src/models/loginModel";
import { createAccount, isCorrectPassword, isNameTaken } from "@/src/services/loginService";
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "@/src/services/loginService";
import { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "@/src/types/loginTypes";
import { logger } from "@/src/utils/logger";
import { version_compare } from "@/src/helpers/inventoryHelpers";
import { sendWsBroadcastTo } from "@/src/services/webService";
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
const account = await Account.findOne({ email: loginRequest.email });
const nonce = Math.round(Math.random() * Number.MAX_SAFE_INTEGER);
const buildLabel: string =
typeof request.query.buildLabel == "string"
@ -42,26 +42,14 @@ export const loginController: RequestHandler = async (request, response) => {
loginRequest.ClientType == "webui-register")
) {
try {
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja";
if (await isNameTaken(name)) {
let suffix = 0;
do {
++suffix;
name = nameFromEmail + suffix;
} while (await isNameTaken(name));
}
const name = await getUsernameFromEmail(loginRequest.email);
const newAccount = await createAccount({
email: loginRequest.email,
password: loginRequest.password,
DisplayName: name,
CountryCode: loginRequest.lang?.toUpperCase() ?? "EN",
ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType,
CrossPlatformAllowed: true,
ForceLogoutVersion: 0,
ConsentNeeded: false,
TrackedSettings: [],
Nonce: nonce,
ClientType: loginRequest.ClientType,
Nonce: createNonce(),
BuildLabel: buildLabel,
LastLogin: new Date()
});
@ -80,38 +68,29 @@ 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;
}
if (loginRequest.ClientType == "webui") {
if (!account.Nonce) {
account.ClientType = "webui";
account.Nonce = nonce;
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;
}
} 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;
}
}
account.ClientType = loginRequest.ClientType;
account.Nonce = nonce;
account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN";
account.BuildLabel = buildLabel;
account.LastLogin = new Date();
}
account.ClientType = loginRequest.ClientType;
account.Nonce = createNonce();
account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN";
account.BuildLabel = buildLabel;
account.LastLogin = new Date();
await account.save();
// Tell WebUI its nonce has been invalidated
sendWsBroadcastTo(account._id.toString(), { logged_out: true });
response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel));
};

View File

@ -1,5 +1,6 @@
import { RequestHandler } from "express";
import { Account } from "@/src/models/loginModel";
import { sendWsBroadcastTo } from "@/src/services/webService";
export const logoutController: RequestHandler = async (req, res) => {
if (!req.query.accountId) {
@ -10,7 +11,7 @@ export const logoutController: RequestHandler = async (req, res) => {
throw new Error("Request is missing nonce parameter");
}
await Account.updateOne(
const stat = await Account.updateOne(
{
_id: req.query.accountId,
Nonce: nonce
@ -19,6 +20,10 @@ export const logoutController: RequestHandler = async (req, res) => {
Nonce: 0
}
);
if (stat.modifiedCount) {
// Tell WebUI its nonce has been invalidated
sendWsBroadcastTo(req.query.accountId as string, { logged_out: true });
}
res.writeHead(200, {
"Content-Type": "text/html",

View File

@ -7,6 +7,7 @@ import { generateRewardSeed, getInventory } from "@/src/services/inventoryServic
import { getInventoryResponse } from "./inventoryController";
import { logger } from "@/src/utils/logger";
import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes";
import { sendWsBroadcastTo } from "@/src/services/webService";
/*
**** INPUT ****
@ -76,6 +77,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
InventoryJson: JSON.stringify(inventoryResponse),
MissionRewards: []
});
sendWsBroadcastTo(account._id.toString(), { update_inventory: true });
return;
}
@ -106,6 +108,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res)
AffiliationMods,
ConquestCompletedMissionsCount
} satisfies IMissionInventoryUpdateResponse);
sendWsBroadcastTo(account._id.toString(), { update_inventory: true });
};
/*

View File

@ -3,6 +3,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes";
import { sendWsBroadcastTo } from "@/src/services/webService";
interface INameWeaponRequest {
ItemName: string;
@ -27,4 +28,5 @@ export const nameWeaponController: RequestHandler = async (req, res) => {
res.json({
InventoryChanges: currencyChanges
});
sendWsBroadcastTo(accountId, { update_inventory: true });
};

View File

@ -1,5 +1,6 @@
import { version_compare } from "@/src/helpers/inventoryHelpers";
import {
antivirusMods,
consumeModCharge,
decodeNemesisGuess,
encodeNemesisGuess,
@ -7,13 +8,13 @@ import {
getKnifeUpgrade,
getNemesisManifest,
getNemesisPasscode,
getNemesisPasscodeModTypes,
GUESS_CORRECT,
GUESS_INCORRECT,
GUESS_NEUTRAL,
GUESS_NONE,
GUESS_WILDCARD,
IKnifeResponse
IKnifeResponse,
parseUpgrade
} from "@/src/helpers/nemesisHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
@ -134,34 +135,38 @@ export const nemesisController: RequestHandler = async (req, res) => {
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":
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusSmallOnSingleUseMod":
antivirusGain += 10;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
break;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusLargeOnSingleUseMod": // Instant Secure
antivirusGain += 15;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
break;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusOnUseMod": // Immuno Shield
antivirusGain += 15;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
break;
case "/Lotus/Upgrades/Mods/DataSpike/Potency/GainAntivirusSmallOnSingleUseMod":
antivirusGain += 10;
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
break;
}
}
inventory.Nemesis!.HenchmenKilled += antivirusGain;
if (inventory.Nemesis!.HenchmenKilled >= 100) {
inventory.Nemesis!.HenchmenKilled = 100;
// Weaken nemesis now.
inventory.Nemesis!.InfNodes = [
{
Node: getNemesisManifest(inventory.Nemesis!.manifest).showdownNode,
Influence: 1
}
];
inventory.Nemesis!.Weakened = true;
const upgrade = getKnifeUpgrade(inventory, dataknifeUpgrades, antivirusMods[passcode]);
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
}
}
if (inventory.Nemesis!.HenchmenKilled >= 100) {
inventory.Nemesis!.HenchmenKilled = 100;
if (inventory.Nemesis!.HenchmenKilled < 100) {
inventory.Nemesis!.InfNodes = getInfNodes(getNemesisManifest(inventory.Nemesis!.manifest), 0);
}
inventory.Nemesis!.InfNodes = getInfNodes(getNemesisManifest(inventory.Nemesis!.manifest), 0);
await inventory.save();
res.json(response);
@ -198,16 +203,40 @@ export const nemesisController: RequestHandler = async (req, res) => {
guess[body.position].result = correct ? GUESS_CORRECT : GUESS_INCORRECT;
inventory.Nemesis!.GuessHistory[inventory.Nemesis!.GuessHistory.length - 1] = encodeNemesisGuess(guess);
// Increase rank if incorrect
let RankIncrease: number | undefined;
if (!correct) {
RankIncrease = 1;
const response: INemesisRequiemResponse = {};
if (correct) {
if (body.position == 2) {
// That was all 3 guesses correct, nemesis is now weakened.
inventory.Nemesis!.InfNodes = [
{
Node: getNemesisManifest(inventory.Nemesis!.manifest).showdownNode,
Influence: 1
}
];
inventory.Nemesis!.Weakened = true;
// Subtract a charge from all requiem mods installed on parazon
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 (let i = 3; i != 6; ++i) {
//logger.debug(`subtracting a charge from ${dataknifeUpgrades[i]}`);
const upgrade = parseUpgrade(inventory, dataknifeUpgrades[i]);
consumeModCharge(response, inventory, upgrade, dataknifeUpgrades);
}
}
} else {
// Guess was incorrect, increase rank
response.RankIncrease = 1;
const manifest = getNemesisManifest(inventory.Nemesis!.manifest);
inventory.Nemesis!.Rank = Math.min(inventory.Nemesis!.Rank + 1, manifest.systemIndexes.length - 1);
inventory.Nemesis!.InfNodes = getInfNodes(manifest, inventory.Nemesis!.Rank);
}
await inventory.save();
res.json({ RankIncrease });
res.json(response);
}
} else if ((req.query.mode as string) == "rs") {
// report spawn; POST but no application data in body
@ -277,36 +306,15 @@ export const nemesisController: RequestHandler = async (req, res) => {
target: inventory.toJSON().Nemesis
});
} else if ((req.query.mode as string) == "w") {
const inventory = await getInventory(
account._id.toString(),
"Nemesis LoadOutPresets CurrentLoadOutIds DataKnives Upgrades RawUpgrades"
);
const inventory = await getInventory(account._id.toString(), "Nemesis");
//const body = getJSONfromString<INemesisWeakenRequest>(String(req.body));
inventory.Nemesis!.InfNodes = [
{
Node: getNemesisManifest(inventory.Nemesis!.manifest).showdownNode,
Influence: 1
}
];
inventory.Nemesis!.Weakened = true;
// As of 38.6.0, this request is no longer sent, instead mode=r already weakens the nemesis if appropriate.
// We always weaken the nemesis in mode=r so simply giving the client back the nemesis.
const response: IKnifeResponse & { target: INemesisClient } = {
const response: INemesisWeakenResponse = {
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)}`);
@ -362,11 +370,19 @@ interface INemesisRequiemRequest {
knife?: IKnife;
}
interface INemesisRequiemResponse extends IKnifeResponse {
RankIncrease?: number;
}
// interface INemesisWeakenRequest {
// target: INemesisClient;
// knife: IKnife;
// }
interface INemesisWeakenResponse extends IKnifeResponse {
target: INemesisClient;
}
interface IKnife {
Item: IEquipmentClient;
Skins: IWeaponSkinClient[];

View File

@ -3,6 +3,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService";
import { IPurchaseRequest } from "@/src/types/purchaseTypes";
import { handlePurchase } from "@/src/services/purchaseService";
import { getInventory } from "@/src/services/inventoryService";
import { sendWsBroadcastTo } from "@/src/services/webService";
export const purchaseController: RequestHandler = async (req, res) => {
const purchaseRequest = JSON.parse(String(req.body)) as IPurchaseRequest;
@ -11,4 +12,5 @@ export const purchaseController: RequestHandler = async (req, res) => {
const response = await handlePurchase(purchaseRequest, inventory);
await inventory.save();
res.json(response);
sendWsBroadcastTo(accountId, { update_inventory: true });
};

View File

@ -1,6 +1,8 @@
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory, updateCurrency } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { sendWsBroadcastTo } from "@/src/services/webService";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { RequestHandler } from "express";
export const renamePetController: RequestHandler = async (req, res) => {
@ -8,13 +10,20 @@ export const renamePetController: RequestHandler = async (req, res) => {
const inventory = await getInventory(accountId, "KubrowPets PremiumCredits PremiumCreditsFree");
const data = getJSONfromString<IRenamePetRequest>(String(req.body));
const details = inventory.KubrowPets.id(data.petId)!.Details!;
details.Name = data.name;
const currencyChanges = updateCurrency(inventory, 15, true);
const inventoryChanges: IInventoryChanges = {};
if (!("webui" in req.query)) {
updateCurrency(inventory, 15, true, inventoryChanges);
}
await inventory.save();
res.json({
...data,
inventoryChanges: currencyChanges
inventoryChanges: inventoryChanges
});
sendWsBroadcastTo(accountId, { update_inventory: true });
};
interface IRenamePetRequest {

View File

@ -24,7 +24,7 @@ export const saveDialogueController: RequestHandler = async (req, res) => {
inventory.DialogueHistory.Dialogues ??= [];
const dialogue = getDialogue(inventory, request.DialogueName);
dialogue.Rank = request.Rank;
dialogue.Chemistry = request.Chemistry;
dialogue.Chemistry += request.Chemistry;
dialogue.QueuedDialogues = request.QueuedDialogues;
for (const bool of request.Booleans) {
dialogue.Booleans.push(bool);

View File

@ -15,6 +15,7 @@ import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes";
import { ExportDojoRecipes } from "warframe-public-export-plus";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { sendWsBroadcastTo } from "@/src/services/webService";
export const sellController: RequestHandler = async (req, res) => {
const payload = JSON.parse(String(req.body)) as ISellRequest;
@ -279,6 +280,7 @@ export const sellController: RequestHandler = async (req, res) => {
res.json({
inventoryChanges: inventoryChanges // "inventoryChanges" for this response instead of the usual "InventoryChanges"
});
sendWsBroadcastTo(accountId, { update_inventory: true });
};
interface ISellRequest {

View File

@ -0,0 +1,22 @@
import { fromMongoDate, fromOid } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { RequestHandler } from "express";
export const setSuitInfectionController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "Suits");
const payload = getJSONfromString<ISetSuitInfectionRequest>(String(req.body));
for (const clientSuit of payload.Suits) {
const dbSuit = inventory.Suits.id(fromOid(clientSuit.ItemId))!;
dbSuit.InfestationDate = fromMongoDate(clientSuit.InfestationDate!);
}
await inventory.save();
res.end();
};
interface ISetSuitInfectionRequest {
Suits: IEquipmentClient[];
}

View File

@ -20,8 +20,8 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
const oldLevel = syndicate.Title ?? 0;
const levelIncrease = data.SacrificeLevel - oldLevel;
if (levelIncrease < 1) {
throw new Error(`syndicate sacrifice needs an increase of at least 1`);
if (levelIncrease < 0) {
throw new Error(`syndicate sacrifice can not decrease level`);
}
if (levelIncrease > 1 && !data.AllowMultiple) {
throw new Error(`desired syndicate level is an increase of ${levelIncrease}, max. allowed increase is 1`);
@ -31,13 +31,13 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
AffiliationTag: data.AffiliationTag,
InventoryChanges: {},
Level: data.SacrificeLevel,
LevelIncrease: levelIncrease,
LevelIncrease: data.SacrificeLevel < 0 ? 1 : levelIncrease,
NewEpisodeReward: false
};
// Process sacrifices and rewards for every level we're reaching
const manifest = ExportSyndicates[data.AffiliationTag];
for (let level = oldLevel + 1; level <= data.SacrificeLevel; ++level) {
for (let level = oldLevel + Math.min(levelIncrease, 1); level <= data.SacrificeLevel; ++level) {
let sacrifice: ISyndicateSacrifice | undefined;
if (level == 0) {
sacrifice = manifest.initiationSacrifice;
@ -94,7 +94,7 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
}
// Commit
syndicate.Title = data.SacrificeLevel;
syndicate.Title = data.SacrificeLevel < 0 ? data.SacrificeLevel + 1 : data.SacrificeLevel;
await inventory.save();
response.json(res);

View File

@ -5,7 +5,7 @@ import { IMiscItem, InventorySlot } from "@/src/types/inventoryTypes/inventoryTy
import { IOid } from "@/src/types/commonTypes";
import { ExportSyndicates, ExportWeapons } from "warframe-public-export-plus";
import { logger } from "@/src/utils/logger";
import { IInventoryChanges } from "@/src/types/purchaseTypes";
import { IAffiliationMods, IInventoryChanges } from "@/src/types/purchaseTypes";
import { EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes";
export const syndicateStandingBonusController: RequestHandler = async (req, res) => {
@ -54,13 +54,14 @@ export const syndicateStandingBonusController: RequestHandler = async (req, res)
inventoryChanges[slotBin] = { count: -1, platinum: 0, Slots: 1 };
}
const affiliationMod = addStanding(inventory, request.Operation.AffiliationTag, gainedStanding, true);
const affiliationMods: IAffiliationMods[] = [];
addStanding(inventory, request.Operation.AffiliationTag, gainedStanding, affiliationMods, true);
await inventory.save();
res.json({
InventoryChanges: inventoryChanges,
AffiliationMods: [affiliationMod]
AffiliationMods: affiliationMods
});
};

View File

@ -0,0 +1,27 @@
import { fromMongoDate, fromOid } from "@/src/helpers/inventoryHelpers";
import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { addMiscItem, getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { RequestHandler } from "express";
export const umbraController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId, "Suits MiscItems");
const payload = getJSONfromString<IUmbraRequest>(String(req.body));
for (const clientSuit of payload.Suits) {
const dbSuit = inventory.Suits.id(fromOid(clientSuit.ItemId))!;
if (clientSuit.UmbraDate) {
addMiscItem(inventory, "/Lotus/Types/Items/MiscItems/UmbraEchoes", -1);
dbSuit.UmbraDate = fromMongoDate(clientSuit.UmbraDate);
} else {
dbSuit.UmbraDate = undefined;
}
}
await inventory.save();
res.end();
};
interface IUmbraRequest {
Suits: IEquipmentClient[];
}

View File

@ -4,6 +4,8 @@ import { getAccountForRequest } from "@/src/services/loginService";
import { addChallenges, getInventory } from "@/src/services/inventoryService";
import { IChallengeProgress, ISeasonChallenge } from "@/src/types/inventoryTypes/inventoryTypes";
import { IAffiliationMods } from "@/src/types/purchaseTypes";
import { getEntriesUnsafe } from "@/src/utils/ts-utils";
import { logger } from "@/src/utils/logger";
export const updateChallengeProgressController: RequestHandler = async (req, res) => {
const challenges = getJSONfromString<IUpdateChallengeProgressRequest>(String(req.body));
@ -11,7 +13,7 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
const inventory = await getInventory(
account._id.toString(),
"ChallengeProgress SeasonChallengeHistory Affiliations"
"ChallengesFixVersion ChallengeProgress SeasonChallengeHistory Affiliations CalendarProgress"
);
let affiliationMods: IAffiliationMods[] = [];
if (challenges.ChallengeProgress) {
@ -22,15 +24,31 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
challenges.SeasonChallengeCompletions
);
}
if (challenges.SeasonChallengeHistory) {
challenges.SeasonChallengeHistory.forEach(({ challenge, id }) => {
const itemIndex = inventory.SeasonChallengeHistory.findIndex(i => i.challenge === challenge);
if (itemIndex !== -1) {
inventory.SeasonChallengeHistory[itemIndex].id = id;
} else {
inventory.SeasonChallengeHistory.push({ challenge, id });
}
});
for (const [key, value] of getEntriesUnsafe(challenges)) {
switch (key) {
case "ChallengesFixVersion":
inventory.ChallengesFixVersion = value;
break;
case "SeasonChallengeHistory":
value!.forEach(({ challenge, id }) => {
const itemIndex = inventory.SeasonChallengeHistory.findIndex(i => i.challenge === challenge);
if (itemIndex !== -1) {
inventory.SeasonChallengeHistory[itemIndex].id = id;
} else {
inventory.SeasonChallengeHistory.push({ challenge, id });
}
});
break;
case "ChallengeProgress":
case "SeasonChallengeCompletions":
case "ChallengePTS":
case "crossPlaySetting":
break;
default:
logger.warn(`unknown challenge progress entry`, { key, value });
}
}
await inventory.save();
@ -40,7 +58,10 @@ export const updateChallengeProgressController: RequestHandler = async (req, res
};
interface IUpdateChallengeProgressRequest {
ChallengePTS?: number;
ChallengesFixVersion?: number;
ChallengeProgress?: IChallengeProgress[];
SeasonChallengeHistory?: ISeasonChallenge[];
SeasonChallengeCompletions?: ISeasonChallenge[];
crossPlaySetting?: string;
}

View File

@ -0,0 +1,34 @@
import { addString } from "@/src/helpers/stringHelpers";
import { getInventory } from "@/src/services/inventoryService";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { addFixedLevelRewards } from "@/src/services/missionInventoryUpdateService";
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
import { IMissionReward } from "@/src/types/missionTypes";
import { RequestHandler } from "express";
import { ExportRegions } from "warframe-public-export-plus";
export const completeAllMissionsController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const inventory = await getInventory(accountId);
const MissionRewards: IMissionReward[] = [];
for (const [tag, node] of Object.entries(ExportRegions)) {
if (!inventory.Missions.find(x => x.Tag == tag)) {
inventory.Missions.push({
Completes: 1,
Tier: 1,
Tag: tag
});
if (node.missionReward) {
console.log(node.missionReward);
addFixedLevelRewards(node.missionReward, inventory, MissionRewards);
}
}
}
for (const reward of MissionRewards) {
await handleStoreItemAcquisition(reward.StoreItem, inventory, reward.ItemCount, undefined, true);
}
addString(inventory.NodeIntrosCompleted, "TeshinHardModeUnlocked");
await inventory.save();
res.end();
};

View File

@ -3,6 +3,7 @@ import { getDict, getItemName, getString } from "@/src/services/itemDataService"
import {
ExportArcanes,
ExportAvionics,
ExportBoosters,
ExportCustoms,
ExportDrones,
ExportGear,
@ -19,7 +20,6 @@ import {
ExportWeapons,
TRelicQuality
} from "warframe-public-export-plus";
import archonCrystalUpgrades from "@/static/fixed_responses/webuiArchonCrystalUpgrades.json";
import allIncarnons from "@/static/fixed_responses/allIncarnonList.json";
interface ListedItem {
@ -35,7 +35,6 @@ interface ListedItem {
}
interface ItemLists {
archonCrystalUpgrades: Record<string, string>;
uniqueLevelCaps: Record<string, number>;
Suits: ListedItem[];
LongGuns: ListedItem[];
@ -55,6 +54,7 @@ interface ItemLists {
KubrowPets: ListedItem[];
EvolutionProgress: ListedItem[];
mods: ListedItem[];
Boosters: ListedItem[];
}
const relicQualitySuffixes: Record<TRelicQuality, string> = {
@ -67,7 +67,6 @@ 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: [],
@ -86,7 +85,8 @@ const getItemListsController: RequestHandler = (req, response) => {
QuestKeys: [],
KubrowPets: [],
EvolutionProgress: [],
mods: []
mods: [],
Boosters: []
};
for (const [uniqueName, item] of Object.entries(ExportWarframes)) {
res[item.productCategory].push({
@ -296,6 +296,13 @@ const getItemListsController: RequestHandler = (req, response) => {
});
}
for (const item of Object.values(ExportBoosters)) {
res.Boosters.push({
uniqueName: item.typeName,
name: getString(item.name, lang)
});
}
response.json(res);
};

View File

@ -128,7 +128,7 @@ export const manageQuestsController: RequestHandler = async (req, res) => {
await completeQuest(inventory, questKey.ItemType);
} else {
const progress = {
c: questManifest.chainStages![currentStage].key ? -1 : 0,
c: 0,
i: false,
m: false,
b: []

View File

@ -12,6 +12,7 @@ export const popArchonCrystalUpgradeController: RequestHandler = async (req, res
);
await inventory.save();
res.end();
return;
}
res.status(400).end();
};

View File

@ -15,6 +15,7 @@ export const pushArchonCrystalUpgradeController: RequestHandler = async (req, re
}
await inventory.save();
res.end();
return;
}
}
res.status(400).end();

View File

@ -0,0 +1,45 @@
import { getAccountIdForRequest } from "@/src/services/loginService";
import { getInventory } from "@/src/services/inventoryService";
import { RequestHandler } from "express";
import { ExportBoosters } from "warframe-public-export-plus";
const I32_MAX = 0x7fffffff;
export const setBoosterController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req);
const requests = req.body as { ItemType: string; ExpiryDate: number }[];
const inventory = await getInventory(accountId, "Boosters");
const boosters = inventory.Boosters;
if (
requests.some(request => {
if (typeof request.ItemType !== "string") return true;
if (Object.entries(ExportBoosters).find(([_, item]) => item.typeName === request.ItemType) === undefined)
return true;
if (typeof request.ExpiryDate !== "number") return true;
if (request.ExpiryDate < 0 || request.ExpiryDate > I32_MAX) return true;
return false;
})
) {
res.status(400).send("Invalid ItemType provided.");
return;
}
const now = Math.floor(Date.now() / 1000);
for (const { ItemType, ExpiryDate } of requests) {
if (ExpiryDate < now) {
// remove expired boosters
const index = boosters.findIndex(item => item.ItemType === ItemType);
if (index !== -1) {
boosters.splice(index, 1);
}
} else {
const boosterItem = boosters.find(item => item.ItemType === ItemType);
if (boosterItem) {
boosterItem.ExpiryDate = ExpiryDate;
} else {
boosters.push({ ItemType, ExpiryDate });
}
}
}
await inventory.save();
res.end();
};

View File

@ -1,15 +1,21 @@
import { RequestHandler } from "express";
import { updateConfig } from "@/src/services/configWatcherService";
import { saveConfig } from "@/src/services/configWatcherService";
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
import { config, IConfig } from "@/src/services/configService";
const updateConfigDataController: RequestHandler = async (req, res) => {
export const updateConfigDataController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req);
if (isAdministrator(account)) {
await updateConfig(String(req.body));
const data = req.body as IUpdateConfigDataRequest;
config[data.key] = data.value;
await saveConfig();
res.end();
} else {
res.status(401).end();
}
};
export { updateConfigDataController };
interface IUpdateConfigDataRequest {
key: keyof IConfig;
value: never;
}

View File

@ -0,0 +1,10 @@
import { args } from "@/src/helpers/commandLineArguments";
import { sendWsBroadcast } from "@/src/services/webService";
import { RequestHandler } from "express";
export const webuiFileChangeDetectedController: RequestHandler = (req, res) => {
if (args.dev && args.secret && req.query.secret == args.secret) {
sendWsBroadcast({ reload: true });
}
res.end();
};

View File

@ -1,6 +1,15 @@
import { RequestHandler } from "express";
import { getWorldState } from "@/src/services/worldStateService";
import { getWorldState, populateFissures } from "@/src/services/worldStateService";
import { version_compare } from "@/src/helpers/inventoryHelpers";
export const worldStateController: RequestHandler = (req, res) => {
res.json(getWorldState(req.query.buildLabel as string | undefined));
export const worldStateController: RequestHandler = async (req, res) => {
const buildLabel = req.query.buildLabel as string | undefined;
const worldState = getWorldState(buildLabel);
// Omitting void fissures for versions prior to Dante Unbound to avoid script errors.
if (!buildLabel || version_compare(buildLabel, "2024.03.24.20.00") >= 0) {
await populateFissures(worldState);
}
res.json(worldState);
};

View File

@ -0,0 +1,23 @@
interface IArguments {
configPath?: string;
dev?: boolean;
secret?: string;
}
export const args: IArguments = {};
for (let i = 2; i < process.argv.length; ) {
switch (process.argv[i++]) {
case "--configPath":
args.configPath = process.argv[i++];
break;
case "--dev":
args.dev = true;
break;
case "--secret":
args.secret = process.argv[i++];
break;
}
}

View File

@ -237,7 +237,7 @@ export const getNemesisPasscode = (nemesis: { fp: bigint; Faction: TNemesisFacti
return passcode;
};
const requiemMods: readonly string[] = [
/*const requiemMods: readonly string[] = [
"/Lotus/Upgrades/Mods/Immortal/ImmortalOneMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalTwoMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalThreeMod",
@ -246,9 +246,9 @@ const requiemMods: readonly string[] = [
"/Lotus/Upgrades/Mods/Immortal/ImmortalSixMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalSevenMod",
"/Lotus/Upgrades/Mods/Immortal/ImmortalEightMod"
];
];*/
const antivirusMods: readonly string[] = [
export const antivirusMods: readonly string[] = [
"/Lotus/Upgrades/Mods/Immortal/AntivirusOneMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusTwoMod",
"/Lotus/Upgrades/Mods/Immortal/AntivirusThreeMod",
@ -259,12 +259,12 @@ const antivirusMods: readonly string[] = [
"/Lotus/Upgrades/Mods/Immortal/AntivirusEightMod"
];
export const getNemesisPasscodeModTypes = (nemesis: { fp: bigint; Faction: TNemesisFaction }): string[] => {
/*export const getNemesisPasscodeModTypes = (nemesis: { fp: bigint; Faction: TNemesisFaction }): string[] => {
const passcode = getNemesisPasscode(nemesis);
return nemesis.Faction == "FC_INFESTATION"
? passcode.map(i => antivirusMods[i])
: passcode.map(i => requiemMods[i]);
};
};*/
// Symbols; 0-7 are the normal requiem mods.
export const GUESS_NONE = 8;
@ -343,6 +343,27 @@ export const getKnifeUpgrade = (
throw new Error(`${type} does not seem to be installed on parazon?!`);
};
export const parseUpgrade = (
inventory: TInventoryDatabaseDocument,
str: string
): { ItemId: IOid; ItemType: string } => {
if (str.length == 24) {
const upgrade = inventory.Upgrades.id(str);
if (upgrade) {
return {
ItemId: { $oid: str },
ItemType: upgrade.ItemType
};
}
throw new Error(`Could not resolve oid ${str}`);
} else {
return {
ItemId: { $oid: "000000000000000000000000" },
ItemType: str
};
}
};
export const consumeModCharge = (
response: IKnifeResponse,
inventory: TInventoryDatabaseDocument,

View File

@ -1,5 +1,4 @@
import path from "path";
export const rootDir = path.join(__dirname, "../..");
export const isDev = path.basename(rootDir) != "build";
export const repoDir = isDev ? rootDir : path.join(rootDir, "..");
export const repoDir = path.basename(rootDir) != "build" ? rootDir : path.join(rootDir, "..");

View File

@ -54,3 +54,9 @@ export const regexEscape = (str: string): string => {
str = str.split("}").join("\\}");
return str;
};
export const addString = (arr: string[], str: string): void => {
if (arr.indexOf(str) == -1) {
arr.push(str);
}
};

View File

@ -10,3 +10,14 @@ export const getMaxStanding = (syndicate: ISyndicate, title: number): number =>
}
return syndicate.titles.find(x => x.level == title)!.maxStanding;
};
export const getMinStanding = (syndicate: ISyndicate, title: number): number => {
if (!syndicate.titles) {
// LibrarySyndicate
return 0;
}
if (title == 0) {
return syndicate.titles.find(x => x.level == -1)!.maxStanding;
}
return syndicate.titles.find(x => x.level == title)!.minStanding;
};

View File

@ -1,9 +1,14 @@
// First, init config.
import { config, loadConfig } from "@/src/services/configService";
import { config, configPath, loadConfig } from "@/src/services/configService";
import fs from "fs";
try {
loadConfig();
} catch (e) {
console.log("ERROR: Failed to load config.json. You can copy config.json.example to create your config.json.");
if (fs.existsSync("config.json")) {
console.log("Failed to load " + configPath + ": " + (e as Error).message);
} else {
console.log("Failed to load " + configPath + ". You can copy config.json.example to create your config file.");
}
process.exit(1);
}
@ -17,6 +22,7 @@ import { JSONStringify } from "json-with-bigint";
import { startWebServer } from "./services/webService";
import { validateConfig } from "@/src/services/configWatcherService";
import { updateWorldStateCollections } from "./services/worldStateService";
// Patch JSON.stringify to work flawlessly with Bigints.
JSON.stringify = JSONStringify;
@ -28,6 +34,11 @@ mongoose
.then(() => {
logger.info("Connected to MongoDB");
startWebServer();
void updateWorldStateCollections();
setInterval(() => {
void updateWorldStateCollections();
}, 60_000);
})
.catch(error => {
if (error instanceof Error) {

View File

@ -251,12 +251,6 @@ const ArchonCrystalUpgradeSchema = new Schema<IArchonCrystalUpgrade>(
{ _id: false }
);
ArchonCrystalUpgradeSchema.set("toJSON", {
transform(_document, returnedObject) {
delete returnedObject.__v;
}
});
const boosterSchema = new Schema<IBooster>(
{
ExpiryDate: Number,
@ -1079,6 +1073,11 @@ EquipmentSchema.set("toJSON", {
if (db.UmbraDate) {
client.UmbraDate = toMongoDate(db.UmbraDate);
}
if (client.ArchonCrystalUpgrades) {
// For some reason, mongoose turns empty objects here into nulls, so we have to fix it.
client.ArchonCrystalUpgrades = client.ArchonCrystalUpgrades.map(x => (x as unknown) ?? {});
}
}
});
@ -1704,7 +1703,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>(
LastInventorySync: Schema.Types.ObjectId,
Mailbox: MailboxSchema,
HandlerPoints: Number,
ChallengesFixVersion: { type: Number, default: 6 },
ChallengesFixVersion: Number,
PlayedParkourTutorial: Boolean,
//ActiveLandscapeTraps: [Schema.Types.Mixed],
//RepVotes: [Schema.Types.Mixed],

View File

@ -11,13 +11,13 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
DisplayName: { type: String, required: true, unique: true },
CountryCode: { type: String, required: true },
CountryCode: { type: String, default: "" },
ClientType: { type: String },
CrossPlatformAllowed: { type: Boolean, required: true },
ForceLogoutVersion: { type: Number, required: true },
CrossPlatformAllowed: { type: Boolean, default: true },
ForceLogoutVersion: { type: Number, default: 0 },
AmazonAuthToken: { type: String },
AmazonRefreshToken: { type: String },
ConsentNeeded: { type: Boolean, required: true },
ConsentNeeded: { type: Boolean, default: false },
TrackedSettings: { type: [String], default: [] },
Nonce: { type: Number, default: 0 },
BuildLabel: String,

View File

@ -0,0 +1,14 @@
import { IFissureDatabase } from "@/src/types/worldStateTypes";
import { model, Schema } from "mongoose";
const fissureSchema = new Schema<IFissureDatabase>({
Activation: Date,
Expiry: Date,
Node: String, // must be unique
Modifier: String,
Hard: Boolean
});
fissureSchema.index({ Expiry: 1 }, { expireAfterSeconds: 0 }); // With this, MongoDB will automatically delete expired entries.
export const Fissure = model<IFissureDatabase>("Fissure", fissureSchema);

View File

@ -33,6 +33,7 @@ import { createAllianceController } from "@/src/controllers/api/createAllianceCo
import { createGuildController } from "@/src/controllers/api/createGuildController";
import { creditsController } from "@/src/controllers/api/creditsController";
import { crewMembersController } from "@/src/controllers/api/crewMembersController";
import { crewShipFusionController } from "@/src/controllers/api/crewShipFusionController";
import { crewShipIdentifySalvageController } from "@/src/controllers/api/crewShipIdentifySalvageController";
import { customizeGuildRanksController } from "@/src/controllers/api/customizeGuildRanksController";
import { customObstacleCourseLeaderboardController } from "@/src/controllers/api/customObstacleCourseLeaderboardController";
@ -132,6 +133,7 @@ import { setPlacedDecoInfoController } from "@/src/controllers/api/setPlacedDeco
import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController";
import { setShipFavouriteLoadoutController } from "@/src/controllers/api/setShipFavouriteLoadoutController";
import { setShipVignetteController } from "@/src/controllers/api/setShipVignetteController";
import { setSuitInfectionController } from "@/src/controllers/api/setSuitInfectionController";
import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController";
import { setWeaponSkillTreeController } from "@/src/controllers/api/setWeaponSkillTreeController";
import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController";
@ -147,6 +149,7 @@ import { syndicateStandingBonusController } from "@/src/controllers/api/syndicat
import { tauntHistoryController } from "@/src/controllers/api/tauntHistoryController";
import { tradingController } from "@/src/controllers/api/tradingController";
import { trainingResultController } from "@/src/controllers/api/trainingResultController";
import { umbraController } from "@/src/controllers/api/umbraController";
import { unlockShipFeatureController } from "@/src/controllers/api/unlockShipFeatureController";
import { updateAlignmentController } from "@/src/controllers/api/updateAlignmentController";
import { updateChallengeProgressController } from "@/src/controllers/api/updateChallengeProgressController";
@ -245,6 +248,7 @@ apiRouter.post("/contributeToVault.php", contributeToVaultController);
apiRouter.post("/createAlliance.php", createAllianceController);
apiRouter.post("/createGuild.php", createGuildController);
apiRouter.post("/crewMembers.php", crewMembersController);
apiRouter.post("/crewShipFusion.php", crewShipFusionController);
apiRouter.post("/crewShipIdentifySalvage.php", crewShipIdentifySalvageController);
apiRouter.post("/customizeGuildRanks.php", customizeGuildRanksController);
apiRouter.post("/customObstacleCourseLeaderboard.php", customObstacleCourseLeaderboardController);
@ -280,6 +284,7 @@ apiRouter.post("/inventorySlots.php", inventorySlotsController);
apiRouter.post("/joinSession.php", joinSessionController);
apiRouter.post("/login.php", loginController);
apiRouter.post("/loginRewardsSelection.php", loginRewardsSelectionController);
apiRouter.post("/logout.php", logoutController); // from ~U16, don't know when they changed it to GET
apiRouter.post("/maturePet.php", maturePetController);
apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController);
apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController);
@ -317,6 +322,7 @@ apiRouter.post("/setPlacedDecoInfo.php", setPlacedDecoInfoController);
apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController);
apiRouter.post("/setShipFavouriteLoadout.php", setShipFavouriteLoadoutController);
apiRouter.post("/setShipVignette.php", setShipVignetteController);
apiRouter.post("/setSuitInfection.php", setSuitInfectionController);
apiRouter.post("/setWeaponSkillTree.php", setWeaponSkillTreeController);
apiRouter.post("/shipDecorations.php", shipDecorationsController);
apiRouter.post("/startCollectibleEntry.php", startCollectibleEntryController);
@ -327,6 +333,7 @@ apiRouter.post("/syndicateSacrifice.php", syndicateSacrificeController);
apiRouter.post("/syndicateStandingBonus.php", syndicateStandingBonusController);
apiRouter.post("/tauntHistory.php", tauntHistoryController);
apiRouter.post("/trainingResult.php", trainingResultController);
apiRouter.post("/umbra.php", umbraController);
apiRouter.post("/unlockShipFeature.php", unlockShipFeatureController);
apiRouter.post("/updateAlignment.php", updateAlignmentController);
apiRouter.post("/updateChallengeProgress.php", updateChallengeProgressController);

View File

@ -11,6 +11,8 @@ import { renameAccountController } from "@/src/controllers/custom/renameAccountC
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
import { webuiFileChangeDetectedController } from "@/src/controllers/custom/webuiFileChangeDetectedController";
import { completeAllMissionsController } from "@/src/controllers/custom/completeAllMissionsController";
import { createAccountController } from "@/src/controllers/custom/createAccountController";
import { createMessageController } from "@/src/controllers/custom/createMessageController";
@ -20,6 +22,7 @@ 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 { setBoosterController } from "@/src/controllers/custom/setBoosterController";
import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
@ -37,6 +40,8 @@ customRouter.get("/renameAccount", renameAccountController);
customRouter.get("/ircDropped", ircDroppedController);
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
customRouter.get("/webuiFileChangeDetected", webuiFileChangeDetectedController);
customRouter.get("/completeAllMissions", completeAllMissionsController);
customRouter.post("/createAccount", createAccountController);
customRouter.post("/createMessage", createMessageController);
@ -46,6 +51,7 @@ customRouter.post("/addXp", addXpController);
customRouter.post("/import", importController);
customRouter.post("/manageQuests", manageQuestsController);
customRouter.post("/setEvolutionProgress", setEvolutionProgressController);
customRouter.post("/setBooster", setBoosterController);
customRouter.get("/config", getConfigDataController);
customRouter.post("/config", updateConfigDataController);

View File

@ -1,6 +1,9 @@
import express from "express";
import path from "path";
import { repoDir, rootDir } from "@/src/helpers/pathHelper";
import { args } from "@/src/helpers/commandLineArguments";
const baseDir = args.dev ? repoDir : rootDir;
const webuiRouter = express.Router();
@ -19,29 +22,29 @@ webuiRouter.use("/webui", (req, res, next) => {
// Serve virtual routes
webuiRouter.get("/webui/inventory", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
res.sendFile(path.join(baseDir, "static/webui/index.html"));
});
webuiRouter.get(/webui\/powersuit\/(.+)/, (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
res.sendFile(path.join(baseDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/mods", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
res.sendFile(path.join(baseDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/settings", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
res.sendFile(path.join(baseDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/quests", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
res.sendFile(path.join(baseDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/cheats", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
res.sendFile(path.join(baseDir, "static/webui/index.html"));
});
webuiRouter.get("/webui/import", (_req, res) => {
res.sendFile(path.join(rootDir, "static/webui/index.html"));
res.sendFile(path.join(baseDir, "static/webui/index.html"));
});
// Serve static files
webuiRouter.use("/webui", express.static(path.join(rootDir, "static/webui")));
webuiRouter.use("/webui", express.static(path.join(baseDir, "static/webui")));
// Serve favicon
webuiRouter.get("/favicon.ico", (_req, res) => {
@ -58,7 +61,7 @@ webuiRouter.get("/webui/riven-tool/RivenParser.js", (_req, res) => {
// Serve translations
webuiRouter.get("/translations/:file", (req, res) => {
res.sendFile(path.join(rootDir, `static/webui/translations/${req.params.file}`));
res.sendFile(path.join(baseDir, `static/webui/translations/${req.params.file}`));
});
export { webuiRouter };

View File

@ -1,8 +1,9 @@
import fs from "fs";
import path from "path";
import { repoDir } from "@/src/helpers/pathHelper";
import { args } from "@/src/helpers/commandLineArguments";
interface IConfig {
export interface IConfig {
mongodbUrl: string;
logger: {
files: boolean;
@ -18,13 +19,16 @@ interface IConfig {
skipTutorial?: boolean;
skipAllDialogue?: boolean;
unlockAllScans?: boolean;
unlockAllMissions?: boolean;
infiniteCredits?: boolean;
infinitePlatinum?: boolean;
infiniteEndo?: boolean;
infiniteRegalAya?: boolean;
infiniteHelminthMaterials?: boolean;
claimingBlueprintRefundsIngredients?: boolean;
dontSubtractPurchaseCreditCost?: boolean;
dontSubtractPurchasePlatinumCost?: boolean;
dontSubtractPurchaseItemCost?: boolean;
dontSubtractPurchaseStandingCost?: boolean;
dontSubtractVoidTraces?: boolean;
dontSubtractConsumables?: boolean;
unlockAllShipFeatures?: boolean;
@ -44,7 +48,9 @@ interface IConfig {
noVendorPurchaseLimits?: boolean;
noDeathMarks?: boolean;
noKimCooldowns?: boolean;
fullyStockedVendors?: boolean;
syndicateMissionsRepeatable?: boolean;
unlockAllProfitTakerStages?: boolean;
instantFinishRivenChallenge?: boolean;
instantResourceExtractorDrones?: boolean;
noResourceExtractorDronesDamage?: boolean;
@ -55,7 +61,14 @@ interface IConfig {
noDojoResearchCosts?: boolean;
noDojoResearchTime?: boolean;
fastClanAscension?: boolean;
missionsCanGiveAllRelics?: boolean;
unlockAllSimarisResearchEntries?: boolean;
spoofMasteryRank?: number;
nightwaveStandingMultiplier?: number;
unfaithfulBugFixes?: {
ignore1999LastRegionPlayed?: boolean;
fixXtraCheeseTimer?: boolean;
};
worldState?: {
creditBoost?: boolean;
affinityBoost?: boolean;
@ -63,11 +76,16 @@ interface IConfig {
starDays?: boolean;
eidolonOverride?: string;
vallisOverride?: string;
duviriOverride?: string;
nightwaveOverride?: string;
circuitGameModes?: string[];
};
dev?: {
keepVendorsExpired?: boolean;
};
}
export const configPath = path.join(repoDir, "config.json");
export const configPath = path.join(repoDir, args.configPath ?? "config.json");
export const config: IConfig = {
mongodbUrl: "mongodb://127.0.0.1:27017/openWF",

View File

@ -2,18 +2,23 @@ import fs from "fs";
import fsPromises from "fs/promises";
import { logger } from "../utils/logger";
import { config, configPath, loadConfig } from "./configService";
import { getWebPorts, startWebServer, stopWebServer } from "./webService";
import { getWebPorts, sendWsBroadcast, startWebServer, stopWebServer } from "./webService";
let amnesia = false;
fs.watchFile(configPath, () => {
fs.watchFile(configPath, (now, then) => {
// https://github.com/oven-sh/bun/issues/20542
if (process.versions.bun && now.mtimeMs == then.mtimeMs) {
return;
}
if (amnesia) {
amnesia = false;
} else {
logger.info("Detected a change to config.json, reloading its contents.");
logger.info("Detected a change to config file, reloading its contents.");
try {
loadConfig();
} catch (e) {
logger.error("Failed to reload config.json. Did you delete it?! Execution cannot continue.");
logger.error("FATAL ERROR: Config failed to be reloaded: " + (e as Error).message);
process.exit(1);
}
validateConfig();
@ -21,7 +26,13 @@ fs.watchFile(configPath, () => {
const webPorts = getWebPorts();
if (config.httpPort != webPorts.http || config.httpsPort != webPorts.https) {
logger.info(`Restarting web server to apply port changes.`);
// Tell webui clients to reload with new port
sendWsBroadcast({ ports: { http: config.httpPort, https: config.httpsPort } });
void stopWebServer().then(startWebServer);
} else {
sendWsBroadcast({ config_reloaded: true });
}
}
});
@ -41,17 +52,11 @@ export const validateConfig = (): void => {
}
}
if (modified) {
logger.info(`Updating config.json to fix some issues with it.`);
logger.info(`Updating config file to fix some issues with it.`);
void saveConfig();
}
};
export const updateConfig = async (data: string): Promise<void> => {
amnesia = true;
await fsPromises.writeFile(configPath, data);
Object.assign(config, JSON.parse(data));
};
export const saveConfig = async (): Promise<void> => {
amnesia = true;
await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2));

View File

@ -50,14 +50,17 @@ export const createNewEventMessages = async (req: Request): Promise<void> => {
await account.save();
};
export const createMessage = async (accountId: string | Types.ObjectId, messages: IMessageCreationTemplate[]) => {
export const createMessage = async (
accountId: string | Types.ObjectId,
messages: IMessageCreationTemplate[]
): Promise<HydratedDocument<IMessageDatabase>[]> => {
const ownerIdMessages = messages.map(m => ({
...m,
ownerId: accountId
}));
const savedMessages = await Inbox.insertMany(ownerIdMessages);
return savedMessages;
return savedMessages as HydratedDocument<IMessageDatabase>[];
};
export interface IMessageCreationTemplate extends Omit<IMessageDatabase, "_id" | "date" | "ownerId"> {

View File

@ -82,11 +82,13 @@ import { handleBundleAcqusition } from "./purchaseService";
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 { getMaxStanding, getMinStanding } from "@/src/helpers/syndicateStandingHelper";
import { getNightwaveSyndicateTag, getWorldState } from "./worldStateService";
import { ICalendarSeason } from "@/src/types/worldStateTypes";
import { generateNemesisProfile, INemesisProfile } from "../helpers/nemesisHelpers";
import { TAccountDocument } from "./loginService";
import { unixTimesInMs } from "../constants/timeConstants";
import { addString } from "../helpers/stringHelpers";
export const createInventory = async (
accountOwnerId: Types.ObjectId,
@ -1202,8 +1204,10 @@ export const addStanding = (
inventory: TInventoryDatabaseDocument,
syndicateTag: string,
gainedStanding: number,
isMedallion: boolean = false
): IAffiliationMods => {
affiliationMods: IAffiliationMods[] = [],
isMedallion: boolean = false,
propagateAlignments: boolean = true
): void => {
let syndicate = inventory.Affiliations.find(x => x.Tag == syndicateTag);
const syndicateMeta = ExportSyndicates[syndicateTag];
@ -1215,6 +1219,10 @@ export const addStanding = (
const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0);
if (syndicate.Standing + gainedStanding > max) gainedStanding = max - syndicate.Standing;
if (syndicate.Title == -2 && syndicate.Standing + gainedStanding < -71000) {
gainedStanding = -71000 + syndicate.Standing;
}
if (!isMedallion || syndicateMeta.medallionsCappedByDailyLimit) {
if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) {
gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin);
@ -1223,10 +1231,27 @@ export const addStanding = (
}
syndicate.Standing += gainedStanding;
return {
const affiliationMod: IAffiliationMods = {
Tag: syndicateTag,
Standing: gainedStanding
};
affiliationMods.push(affiliationMod);
if (syndicateMeta.alignments) {
if (propagateAlignments) {
for (const [tag, factor] of Object.entries(syndicateMeta.alignments)) {
addStanding(inventory, tag, gainedStanding * factor, affiliationMods, isMedallion, false);
}
} else {
while (syndicate.Standing < getMinStanding(syndicateMeta, syndicate.Title ?? 0)) {
syndicate.Title ??= 0;
syndicate.Title -= 1;
affiliationMod.Title ??= 0;
affiliationMod.Title -= 1;
logger.debug(`${syndicateTag} is decreasing to title ${syndicate.Title} after applying alignment`);
}
}
}
};
// TODO: AffiliationMods support (Nightwave).
@ -1557,7 +1582,7 @@ export const addMiscItem = (
inventory: TInventoryDatabaseDocument,
type: string,
count: number,
inventoryChanges: IInventoryChanges
inventoryChanges: IInventoryChanges = {}
): void => {
const miscItemChanges: IMiscItem[] = [
{
@ -1708,12 +1733,27 @@ export const addFocusXpIncreases = (inventory: TInventoryDatabaseDocument, focus
AP_ANY
}
inventory.FocusXP ??= { AP_ATTACK: 0, AP_DEFENSE: 0, AP_TACTIC: 0, AP_POWER: 0, AP_WARD: 0 };
inventory.FocusXP.AP_ATTACK += focusXpPlus[FocusType.AP_ATTACK];
inventory.FocusXP.AP_DEFENSE += focusXpPlus[FocusType.AP_DEFENSE];
inventory.FocusXP.AP_TACTIC += focusXpPlus[FocusType.AP_TACTIC];
inventory.FocusXP.AP_POWER += focusXpPlus[FocusType.AP_POWER];
inventory.FocusXP.AP_WARD += focusXpPlus[FocusType.AP_WARD];
inventory.FocusXP ??= {};
if (focusXpPlus[FocusType.AP_ATTACK]) {
inventory.FocusXP.AP_ATTACK ??= 0;
inventory.FocusXP.AP_ATTACK += focusXpPlus[FocusType.AP_ATTACK];
}
if (focusXpPlus[FocusType.AP_DEFENSE]) {
inventory.FocusXP.AP_DEFENSE ??= 0;
inventory.FocusXP.AP_DEFENSE += focusXpPlus[FocusType.AP_DEFENSE];
}
if (focusXpPlus[FocusType.AP_TACTIC]) {
inventory.FocusXP.AP_TACTIC ??= 0;
inventory.FocusXP.AP_TACTIC += focusXpPlus[FocusType.AP_TACTIC];
}
if (focusXpPlus[FocusType.AP_POWER]) {
inventory.FocusXP.AP_POWER ??= 0;
inventory.FocusXP.AP_POWER += focusXpPlus[FocusType.AP_POWER];
}
if (focusXpPlus[FocusType.AP_WARD]) {
inventory.FocusXP.AP_WARD ??= 0;
inventory.FocusXP.AP_WARD += focusXpPlus[FocusType.AP_WARD];
}
if (!config.noDailyFocusLimit) {
inventory.DailyFocus -= focusXpPlus.reduce((a, b) => a + b, 0);
@ -1745,6 +1785,10 @@ export const addChallenges = (
} else {
inventory.ChallengeProgress.push({ Name, Progress });
}
if (Name.startsWith("Calendar")) {
addString(getCalendarProgress(inventory).SeasonProgress.ActivatedChallenges, Name);
}
});
const affiliationMods: IAffiliationMods[] = [];
@ -1773,25 +1817,29 @@ export const addChallenges = (
}) - 1
];
}
affiliation.Standing += meta.standing!;
const standingToAdd = meta.standing! * (config.nightwaveStandingMultiplier ?? 1);
affiliation.Standing += standingToAdd;
if (affiliationMods.length == 0) {
affiliationMods.push({ Tag: nightwaveSyndicateTag });
}
affiliationMods[0].Standing ??= 0;
affiliationMods[0].Standing += meta.standing!;
affiliationMods[0].Standing += standingToAdd;
}
}
}
return affiliationMods;
};
export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes }: IMission): void => {
export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes, Tier }: IMission): void => {
const { Missions } = inventory;
const itemIndex = Missions.findIndex(item => item.Tag === Tag);
if (itemIndex !== -1) {
Missions[itemIndex].Completes += Completes;
if (Tier) {
Missions[itemIndex].Tier = Tier;
}
} else {
Missions.push({ Tag, Completes });
}
@ -1987,6 +2035,20 @@ export const getCalendarProgress = (inventory: TInventoryDatabaseDocument): ICal
return inventory.CalendarProgress;
};
export const checkCalendarChallengeCompletion = (
calendarProgress: ICalendarProgress,
currentSeason: ICalendarSeason
): void => {
const dayIndex = calendarProgress.SeasonProgress.LastCompletedDayIdx + 1;
if (calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx >= dayIndex) {
const day = currentSeason.Days[dayIndex];
if (day.events.length != 0 && day.events[0].type == "CET_CHALLENGE") {
//logger.debug(`already completed the challenge, skipping ahead`);
calendarProgress.SeasonProgress.LastCompletedDayIdx++;
}
}
};
export const giveNemesisWeaponRecipe = (
inventory: TInventoryDatabaseDocument,
weaponType: string,

View File

@ -18,6 +18,23 @@ export const isNameTaken = async (name: string): Promise<boolean> => {
return !!(await Account.findOne({ DisplayName: name }));
};
export const createNonce = (): number => {
return Math.round(Math.random() * Number.MAX_SAFE_INTEGER);
};
export const getUsernameFromEmail = async (email: string): Promise<string> => {
const nameFromEmail = email.substring(0, email.indexOf("@"));
let name = nameFromEmail || email.substring(1) || "SpaceNinja";
if (await isNameTaken(name)) {
let suffix = 0;
do {
++suffix;
name = nameFromEmail + suffix;
} while (await isNameTaken(name));
}
return nameFromEmail;
};
export const createAccount = async (accountData: IDatabaseAccountRequiredFields): Promise<IDatabaseAccountJson> => {
const account = new Account(accountData);
try {

View File

@ -2,6 +2,7 @@ import {
ExportEnemies,
ExportFusionBundles,
ExportRegions,
ExportRelics,
ExportRewards,
IMissionReward as IMissionRewardExternal,
IRegion,
@ -32,6 +33,7 @@ import {
addSkin,
addStanding,
applyClientEquipmentUpdates,
checkCalendarChallengeCompletion,
combineInventoryChanges,
generateRewardSeed,
getCalendarProgress,
@ -86,7 +88,7 @@ const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[]
if (rewardInfo.VaultsCracked) {
const rotations: number[] = [];
for (let i = 0; i != rewardInfo.VaultsCracked; ++i) {
rotations.push(i);
rotations.push(Math.min(i, 2));
}
return rotations;
}
@ -266,7 +268,9 @@ export const addMissionInventoryUpdates = async (
addMissionComplete(inventory, value);
break;
case "LastRegionPlayed":
inventory.LastRegionPlayed = value;
if (!(config.unfaithfulBugFixes?.ignore1999LastRegionPlayed && value === "1999MapName")) {
inventory.LastRegionPlayed = value;
}
break;
case "RawUpgrades":
addMods(inventory, value);
@ -627,11 +631,11 @@ export const addMissionInventoryUpdates = async (
}
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);
}
const currentSeason = getWorldState().KnownCalendarSeasons[0];
calendarProgress.SeasonProgress.LastCompletedChallengeDayIdx = currentSeason.Days.findIndex(
x => x.events[0].challenge == value[value.length - 1].challenge
);
checkCalendarChallengeCompletion(calendarProgress, currentSeason);
break;
}
case "duviriCaveOffers": {
@ -1212,8 +1216,10 @@ export const addMissionRewards = async (
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;
if (jobType.endsWith("VaultBounty")) {
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));
if (
@ -1236,19 +1242,18 @@ export const addMissionRewards = async (
SyndicateXPItemReward = medallionAmount;
} 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,
Math.floor(currentJob.xpAmounts[rewardInfo.JobStage] / (rewardInfo.Q ? 0.8 : 1)),
AffiliationMods
);
} else {
if (jobType.endsWith("Heists/HeistProfitTakerBountyOne") && rewardInfo.JobStage === 2) {
AffiliationMods.push(addStanding(inventory, syndicateEntry.Tag, 1000));
addStanding(inventory, syndicateEntry.Tag, 1000, AffiliationMods);
}
if (jobType.endsWith("Hunts/AllTeralystsHunt") && rewardInfo.JobStage === 2) {
AffiliationMods.push(addStanding(inventory, syndicateEntry.Tag, 5000));
addStanding(inventory, syndicateEntry.Tag, 5000, AffiliationMods);
}
if (
[
@ -1259,7 +1264,7 @@ export const addMissionRewards = async (
"Heists/HeistExploiterBountyOne"
].some(ending => jobType.endsWith(ending))
) {
AffiliationMods.push(addStanding(inventory, syndicateEntry.Tag, 1000));
addStanding(inventory, syndicateEntry.Tag, 1000, AffiliationMods);
}
}
}
@ -1267,9 +1272,9 @@ export const addMissionRewards = async (
}
if (rewardInfo.challengeMissionId) {
const [syndicateTag, tierStr, chemistryStr] = rewardInfo.challengeMissionId.split("_");
const [syndicateTag, tierStr, chemistryBuddyStr] = rewardInfo.challengeMissionId.split("_");
const tier = Number(tierStr);
const chemistry = Number(chemistryStr);
const chemistryBuddy = Number(chemistryBuddyStr);
const isSteelPath = missions?.Tier;
if (syndicateTag === "ZarimanSyndicate") {
let medallionAmount = tier + 1;
@ -1284,24 +1289,21 @@ export const addMissionRewards = async (
let standingAmount = (tier + 1) * 1000;
if (tier > 5) standingAmount = 7500; // InfestedLichBounty
if (isSteelPath) standingAmount *= 1.5;
AffiliationMods.push(addStanding(inventory, syndicateTag, standingAmount));
addStanding(inventory, syndicateTag, standingAmount, AffiliationMods);
}
if (syndicateTag == "HexSyndicate" && chemistry && tier < 6) {
const seed = getWorldState().SyndicateMissions.find(x => x.Tag == "HexSyndicate")!.Seed;
const { nodes, buddies } = getHexBounties(seed);
const buddy = buddies[tier];
logger.debug(`Hex seed is ${seed}, giving chemistry for ${buddy}`);
if (missions?.Tag != nodes[tier]) {
logger.warn(
`Uh-oh, tier ${tier} bounty should've been on ${nodes[tier]} but you were just on ${missions?.Tag}`
);
}
const tomorrowAt0Utc = config.noKimCooldowns
? Date.now()
: (Math.trunc(Date.now() / 86400_000) + 1) * 86400_000;
if (syndicateTag == "HexSyndicate" && tier < 6) {
const buddy = chemistryBuddies[chemistryBuddy];
const dialogue = getDialogue(inventory, buddy);
dialogue.Chemistry += chemistry;
dialogue.BountyChemExpiry = new Date(tomorrowAt0Utc);
if (Date.now() >= dialogue.BountyChemExpiry.getTime()) {
logger.debug(`Giving 20 chemistry for ${buddy}`);
const tomorrowAt0Utc = config.noKimCooldowns
? Date.now()
: (Math.trunc(Date.now() / 86400_000) + 1) * 86400_000;
dialogue.Chemistry += 20;
dialogue.BountyChemExpiry = new Date(tomorrowAt0Utc);
} else {
logger.debug(`Already got today's chemistry for ${buddy}`);
}
}
if (isSteelPath) {
await addItem(inventory, "/Lotus/Types/Items/MiscItems/SteelEssence", 1);
@ -1376,7 +1378,7 @@ export const addFixedLevelRewards = (
if (rewards.countedItems) {
for (const item of rewards.countedItems) {
MissionRewards.push({
StoreItem: `/Lotus/StoreItems${item.ItemType.substring("Lotus/".length)}`,
StoreItem: toStoreItem(item.ItemType),
ItemCount: item.ItemCount
});
}
@ -1564,8 +1566,10 @@ function getRandomMissionDrops(
let job = syndicateEntry.Jobs[RewardInfo.JobTier!];
if (syndicateEntry.Tag === "EntratiSyndicate") {
const vault = syndicateEntry.Jobs.find(j => j.locationTag === locationTag);
if (vault && locationTag) job = vault;
if (jobType.endsWith("VaultBounty")) {
const vault = syndicateEntry.Jobs.find(j => j.locationTag === locationTag);
if (vault) job = vault;
}
// if (
// [
// "DeimosRuinsExterminateBounty",
@ -1635,7 +1639,19 @@ function getRandomMissionDrops(
}
rewardManifests = [job.rewards];
if (job.xpAmounts.length > 1) {
rotations = [RewardInfo.JobStage! % (job.xpAmounts.length - 1)];
const curentStage = RewardInfo.JobStage! + 1;
const totalStage = job.xpAmounts.length;
let tableIndex = 1; // Stage 2, Stage 3 of 4, and Stage 3 of 5
if (curentStage == 1) {
tableIndex = 0;
} else if (curentStage == totalStage) {
tableIndex = 3;
} else if (totalStage == 5 && curentStage == 4) {
tableIndex = 2;
}
rotations = [tableIndex];
} else {
rotations = [0];
}
@ -1644,11 +1660,7 @@ function getRandomMissionDrops(
(RewardInfo.JobStage === job.xpAmounts.length - 1 || job.isVault) &&
!isEndlessJob
) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (ExportRewards[job.rewards]) {
rewardManifests.push(job.rewards);
rotations.push(ExportRewards[job.rewards].length - 1);
}
rotations.push(3);
}
}
}
@ -1813,6 +1825,23 @@ function getRandomMissionDrops(
drops.push({ StoreItem: drop.type, ItemCount: drop.itemCount });
}
}
if (config.missionsCanGiveAllRelics) {
for (const drop of drops) {
const itemType = fromStoreItem(drop.StoreItem);
if (itemType in ExportRelics) {
const relic = ExportRelics[itemType];
const replacement = getRandomElement(
Object.entries(ExportRelics).filter(
arr => arr[1].era == relic.era && arr[1].quality == relic.quality
)
)!;
logger.debug(`replacing ${relic.era} ${relic.category} with ${replacement[1].category}`);
drop.StoreItem = toStoreItem(replacement[0]);
}
}
}
return drops;
}
@ -1865,7 +1894,16 @@ const libraryPersonalTargetToAvatar: Record<string, string> = {
"/Lotus/Types/Enemies/Corpus/Spaceman/AIWeek/NullifySpacemanAvatar"
};
const node_excluded_buddies: Record<string, string> = {
const chemistryBuddies: readonly string[] = [
"/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
"/Lotus/Types/Gameplay/1999Wf/Dialogue/AoiDialogue_rom.dialogue",
"/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
"/Lotus/Types/Gameplay/1999Wf/Dialogue/EleanorDialogue_rom.dialogue",
"/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
"/Lotus/Types/Gameplay/1999Wf/Dialogue/QuincyDialogue_rom.dialogue"
];
/*const node_excluded_buddies: Record<string, string> = {
SolNode856: "/Lotus/Types/Gameplay/1999Wf/Dialogue/ArthurDialogue_rom.dialogue",
SolNode852: "/Lotus/Types/Gameplay/1999Wf/Dialogue/LettieDialogue_rom.dialogue",
SolNode851: "/Lotus/Types/Gameplay/1999Wf/Dialogue/JabirDialogue_rom.dialogue",
@ -1915,4 +1953,4 @@ const getHexBounties = (seed: number): { nodes: string[]; buddies: string[] } =>
}
}
return { nodes, buddies };
};
};*/

View File

@ -11,7 +11,13 @@ import {
import { getRandomWeightedRewardUc } from "@/src/services/rngService";
import { applyStandingToVendorManifest, getVendorManifestByOid } from "@/src/services/serversideVendorsService";
import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { IPurchaseRequest, IPurchaseResponse, SlotPurchase, IInventoryChanges } from "@/src/types/purchaseTypes";
import {
IPurchaseRequest,
IPurchaseResponse,
SlotPurchase,
IInventoryChanges,
PurchaseSource
} from "@/src/types/purchaseTypes";
import { logger } from "@/src/utils/logger";
import worldState from "@/static/fixed_responses/worldState/worldState.json";
import {
@ -52,7 +58,7 @@ export const handlePurchase = async (
const prePurchaseInventoryChanges: IInventoryChanges = {};
let seed: bigint | undefined;
if (purchaseRequest.PurchaseParams.Source == 7) {
if (purchaseRequest.PurchaseParams.Source == PurchaseSource.Vendor) {
let manifest = getVendorManifestByOid(purchaseRequest.PurchaseParams.SourceId!);
if (manifest) {
manifest = applyStandingToVendorManifest(inventory, manifest);
@ -67,25 +73,25 @@ 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 (!config.dontSubtractPurchaseCreditCost) {
if (offer.RegularPrice) {
updateCurrency(inventory, offer.RegularPrice[0], false, prePurchaseInventoryChanges);
}
}
if (offer.PremiumPrice) {
combineInventoryChanges(
prePurchaseInventoryChanges,
updateCurrency(inventory, offer.PremiumPrice[0], true)
);
if (!config.dontSubtractPurchasePlatinumCost) {
if (offer.PremiumPrice) {
updateCurrency(inventory, offer.PremiumPrice[0], true, prePurchaseInventoryChanges);
}
}
if (offer.ItemPrices) {
handleItemPrices(
inventory,
offer.ItemPrices,
purchaseRequest.PurchaseParams.Quantity,
prePurchaseInventoryChanges
);
if (!config.dontSubtractPurchaseItemCost) {
if (offer.ItemPrices) {
handleItemPrices(
inventory,
offer.ItemPrices,
purchaseRequest.PurchaseParams.Quantity,
prePurchaseInventoryChanges
);
}
}
if (offer.LocTagRandSeed !== undefined) {
seed = BigInt(offer.LocTagRandSeed);
@ -160,18 +166,15 @@ export const handlePurchase = async (
);
combineInventoryChanges(purchaseResponse.InventoryChanges, prePurchaseInventoryChanges);
const currencyChanges = updateCurrency(
updateCurrency(
inventory,
purchaseRequest.PurchaseParams.ExpectedPrice,
purchaseRequest.PurchaseParams.UsePremium
purchaseRequest.PurchaseParams.UsePremium,
prePurchaseInventoryChanges
);
purchaseResponse.InventoryChanges = {
...currencyChanges,
...purchaseResponse.InventoryChanges
};
switch (purchaseRequest.PurchaseParams.Source) {
case 1: {
case PurchaseSource.VoidTrader: {
if (purchaseRequest.PurchaseParams.SourceId! != worldState.VoidTraders[0]._id.$oid) {
throw new Error("invalid request source");
}
@ -179,25 +182,26 @@ export const handlePurchase = async (
x => x.ItemType == purchaseRequest.PurchaseParams.StoreItem
);
if (offer) {
combineInventoryChanges(
purchaseResponse.InventoryChanges,
updateCurrency(inventory, offer.RegularPrice, false)
);
if (!config.dontSubtractPurchaseCreditCost) {
updateCurrency(inventory, offer.RegularPrice, false, purchaseResponse.InventoryChanges);
}
if (purchaseRequest.PurchaseParams.ExpectedPrice) {
throw new Error(`vendor purchase should not have an expected price`);
}
const invItem: IMiscItem = {
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
ItemCount: offer.PrimePrice * purchaseRequest.PurchaseParams.Quantity * -1
};
addMiscItems(inventory, [invItem]);
purchaseResponse.InventoryChanges.MiscItems ??= [];
purchaseResponse.InventoryChanges.MiscItems.push(invItem);
if (!config.dontSubtractPurchaseItemCost) {
const invItem: IMiscItem = {
ItemType: "/Lotus/Types/Items/MiscItems/PrimeBucks",
ItemCount: offer.PrimePrice * purchaseRequest.PurchaseParams.Quantity * -1
};
addMiscItems(inventory, [invItem]);
purchaseResponse.InventoryChanges.MiscItems ??= [];
purchaseResponse.InventoryChanges.MiscItems.push(invItem);
}
}
break;
}
case 2:
case PurchaseSource.SyndicateFavor:
{
const syndicateTag = purchaseRequest.PurchaseParams.SyndicateTag!;
if (purchaseRequest.PurchaseParams.UseFreeFavor!) {
@ -211,7 +215,7 @@ export const handlePurchase = async (
Title: lastTitle
}
];
} else {
} else if (!config.dontSubtractPurchaseStandingCost) {
const syndicate = ExportSyndicates[syndicateTag];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (syndicate) {
@ -234,24 +238,18 @@ export const handlePurchase = async (
}
}
break;
case 7:
case PurchaseSource.Vendor:
if (purchaseRequest.PurchaseParams.SourceId! in ExportVendors) {
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem);
if (offer) {
if (typeof offer.credits == "number") {
combineInventoryChanges(
purchaseResponse.InventoryChanges,
updateCurrency(inventory, offer.credits, false)
);
if (typeof offer.credits == "number" && !config.dontSubtractPurchaseCreditCost) {
updateCurrency(inventory, offer.credits, false, purchaseResponse.InventoryChanges);
}
if (typeof offer.platinum == "number") {
combineInventoryChanges(
purchaseResponse.InventoryChanges,
updateCurrency(inventory, offer.platinum, true)
);
if (typeof offer.platinum == "number" && !config.dontSubtractPurchasePlatinumCost) {
updateCurrency(inventory, offer.platinum, true, purchaseResponse.InventoryChanges);
}
if (offer.itemPrices) {
if (offer.itemPrices && !config.dontSubtractPurchaseItemCost) {
handleItemPrices(
inventory,
offer.itemPrices,
@ -265,7 +263,7 @@ export const handlePurchase = async (
throw new Error(`vendor purchase should not have an expected price`);
}
break;
case 18: {
case PurchaseSource.PrimeVaultTrader: {
if (purchaseRequest.PurchaseParams.SourceId! != worldState.PrimeVaultTraders[0]._id.$oid) {
throw new Error("invalid request source");
}
@ -278,15 +276,17 @@ export const handlePurchase = async (
);
if (offer) {
if (offer.RegularPrice) {
const invItem: IMiscItem = {
ItemType: "/Lotus/Types/Items/MiscItems/SchismKey",
ItemCount: offer.RegularPrice * purchaseRequest.PurchaseParams.Quantity * -1
};
if (!config.dontSubtractPurchaseItemCost) {
const invItem: IMiscItem = {
ItemType: "/Lotus/Types/Items/MiscItems/SchismKey",
ItemCount: offer.RegularPrice * purchaseRequest.PurchaseParams.Quantity * -1
};
addMiscItems(inventory, [invItem]);
addMiscItems(inventory, [invItem]);
purchaseResponse.InventoryChanges.MiscItems ??= [];
purchaseResponse.InventoryChanges.MiscItems.push(invItem);
purchaseResponse.InventoryChanges.MiscItems ??= [];
purchaseResponse.InventoryChanges.MiscItems.push(invItem);
}
} else if (!config.infiniteRegalAya) {
inventory.PrimeTokens -= offer.PrimePrice! * purchaseRequest.PurchaseParams.Quantity;

View File

@ -1,64 +1,12 @@
import { unixTimesInMs } from "@/src/constants/timeConstants";
import { isDev } from "@/src/helpers/pathHelper";
import { args } from "@/src/helpers/commandLineArguments";
import { catBreadHash } from "@/src/helpers/stringHelpers";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { mixSeeds, SRng } from "@/src/services/rngService";
import { IMongoDate } from "@/src/types/commonTypes";
import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes";
import { logger } from "@/src/utils/logger";
import { ExportVendors, IRange, IVendor } from "warframe-public-export-plus";
import ArchimedeanVendorManifest from "@/static/fixed_responses/getVendorInfo/ArchimedeanVendorManifest.json";
import DeimosEntratiFragmentVendorProductsManifest from "@/static/fixed_responses/getVendorInfo/DeimosEntratiFragmentVendorProductsManifest.json";
import DeimosHivemindCommisionsManifestFishmonger from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestFishmonger.json";
import DeimosHivemindCommisionsManifestPetVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestPetVendor.json";
import DeimosHivemindCommisionsManifestProspector from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestProspector.json";
import DeimosHivemindCommisionsManifestTokenVendor from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestTokenVendor.json";
import DeimosHivemindCommisionsManifestWeaponsmith from "@/static/fixed_responses/getVendorInfo/DeimosHivemindCommisionsManifestWeaponsmith.json";
import DeimosHivemindTokenVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosHivemindTokenVendorManifest.json";
import DeimosPetVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosPetVendorManifest.json";
import DeimosProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/DeimosProspectorVendorManifest.json";
import DuviriAcrithisVendorManifest from "@/static/fixed_responses/getVendorInfo/DuviriAcrithisVendorManifest.json";
import EntratiLabsEntratiLabsCommisionsManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabsCommisionsManifest.json";
import EntratiLabsEntratiLabVendorManifest from "@/static/fixed_responses/getVendorInfo/EntratiLabsEntratiLabVendorManifest.json";
import HubsIronwakeDondaVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsIronwakeDondaVendorManifest.json";
import HubsRailjackCrewMemberVendorManifest from "@/static/fixed_responses/getVendorInfo/HubsRailjackCrewMemberVendorManifest.json";
import MaskSalesmanManifest from "@/static/fixed_responses/getVendorInfo/MaskSalesmanManifest.json";
import Nova1999ConquestShopManifest from "@/static/fixed_responses/getVendorInfo/Nova1999ConquestShopManifest.json";
import OstronPetVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronPetVendorManifest.json";
import OstronProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/OstronProspectorVendorManifest.json";
import SolarisDebtTokenVendorRepossessionsManifest from "@/static/fixed_responses/getVendorInfo/SolarisDebtTokenVendorRepossessionsManifest.json";
import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorInfo/SolarisProspectorVendorManifest.json";
import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json";
import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json";
import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json";
const rawVendorManifests: IVendorManifest[] = [
ArchimedeanVendorManifest,
DeimosEntratiFragmentVendorProductsManifest,
DeimosHivemindCommisionsManifestFishmonger,
DeimosHivemindCommisionsManifestPetVendor,
DeimosHivemindCommisionsManifestProspector,
DeimosHivemindCommisionsManifestTokenVendor,
DeimosHivemindCommisionsManifestWeaponsmith,
DeimosHivemindTokenVendorManifest,
DeimosPetVendorManifest,
DeimosProspectorVendorManifest,
DuviriAcrithisVendorManifest,
EntratiLabsEntratiLabsCommisionsManifest,
EntratiLabsEntratiLabVendorManifest,
HubsIronwakeDondaVendorManifest, // uses preprocessing
HubsRailjackCrewMemberVendorManifest,
MaskSalesmanManifest,
Nova1999ConquestShopManifest,
OstronPetVendorManifest,
OstronProspectorVendorManifest,
SolarisDebtTokenVendorRepossessionsManifest,
SolarisProspectorVendorManifest,
Temple1999VendorManifest,
TeshinHardModeVendorManifest, // uses preprocessing
ZarimanCommisionsManifestArchimedean
];
import { ExportVendors, IRange, IVendor, IVendorOffer } from "warframe-public-export-plus";
import { config } from "./configService";
interface IGeneratableVendorInfo extends Omit<IVendorInfo, "ItemManifest" | "Expiry"> {
cycleOffset?: number;
@ -83,10 +31,6 @@ const generatableVendors: IGeneratableVendorInfo[] = [
cycleOffset: 1744934400_000,
cycleDuration: 4 * unixTimesInMs.day
}
// {
// _id: { $oid: "5dbb4c41e966f7886c3ce939" },
// TypeName: "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest"
// }
];
const getVendorOid = (typeName: string): string => {
@ -101,60 +45,60 @@ const gcd = (a: number, b: number): number => {
const getCycleDuration = (manifest: IVendor): number => {
let dur = 0;
for (const item of manifest.items) {
if (typeof item.durationHours != "number") {
if (item.alwaysOffered) {
continue;
}
const durationHours = item.rotatedWeekly ? 168 : item.durationHours;
if (typeof durationHours != "number") {
dur = 1;
break;
}
if (dur != item.durationHours) {
dur = gcd(dur, item.durationHours);
if (dur != durationHours) {
dur = gcd(dur, durationHours);
}
}
return dur * unixTimesInMs.hour;
};
export const getVendorManifestByTypeName = (typeName: string): IVendorManifest | undefined => {
for (const vendorManifest of rawVendorManifests) {
if (vendorManifest.VendorInfo.TypeName == typeName) {
return preprocessVendorManifest(vendorManifest);
}
}
export const getVendorManifestByTypeName = (typeName: string, fullStock?: boolean): IVendorManifest | undefined => {
for (const vendorInfo of generatableVendors) {
if (vendorInfo.TypeName == typeName) {
return generateVendorManifest(vendorInfo);
return generateVendorManifest(vendorInfo, fullStock ?? config.fullyStockedVendors);
}
}
if (typeName in ExportVendors) {
const manifest = ExportVendors[typeName];
return generateVendorManifest({
_id: { $oid: getVendorOid(typeName) },
TypeName: typeName,
RandomSeedType: manifest.randomSeedType,
cycleDuration: getCycleDuration(manifest)
});
return generateVendorManifest(
{
_id: { $oid: getVendorOid(typeName) },
TypeName: typeName,
RandomSeedType: manifest.randomSeedType,
cycleDuration: getCycleDuration(manifest)
},
fullStock ?? config.fullyStockedVendors
);
}
return undefined;
};
export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined => {
for (const vendorManifest of rawVendorManifests) {
if (vendorManifest.VendorInfo._id.$oid == oid) {
return preprocessVendorManifest(vendorManifest);
}
}
for (const vendorInfo of generatableVendors) {
if (vendorInfo._id.$oid == oid) {
return generateVendorManifest(vendorInfo);
return generateVendorManifest(vendorInfo, config.fullyStockedVendors);
}
}
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: getCycleDuration(manifest)
});
return generateVendorManifest(
{
_id: { $oid: typeNameOid },
TypeName: typeName,
RandomSeedType: manifest.randomSeedType,
cycleDuration: getCycleDuration(manifest)
},
config.fullyStockedVendors
);
}
}
return undefined;
@ -197,30 +141,6 @@ export const applyStandingToVendorManifest = (
};
};
const preprocessVendorManifest = (originalManifest: IVendorManifest): IVendorManifest => {
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);
}
return manifest;
}
return originalManifest;
};
const refreshExpiry = (expiry: IMongoDate): void => {
const period = parseInt(expiry.$date.$numberLong);
if (Date.now() >= period) {
const epoch = 1734307200_000; // Monday (for weekly schedules)
const iteration = Math.trunc((Date.now() - epoch) / period);
const start = epoch + iteration * period;
const end = start + period;
expiry.$date.$numberLong = end.toString();
}
};
const toRange = (value: IRange | number): IRange => {
if (typeof value == "number") {
return { minValue: value, maxValue: value };
@ -228,9 +148,54 @@ const toRange = (value: IRange | number): IRange => {
return value;
};
const getCycleDurationRange = (manifest: IVendor): IRange | undefined => {
const res: IRange = { minValue: Number.MAX_SAFE_INTEGER, maxValue: 0 };
for (const offer of manifest.items) {
if (offer.durationHours) {
const range = toRange(offer.durationHours);
if (res.minValue > range.minValue) {
res.minValue = range.minValue;
}
if (res.maxValue < range.maxValue) {
res.maxValue = range.maxValue;
}
}
}
return res.maxValue != 0 ? res : undefined;
};
type TOfferId = string;
const getOfferId = (offer: IVendorOffer | IItemManifest): TOfferId => {
if ("storeItem" in offer) {
// IVendorOffer
return offer.storeItem + "x" + offer.quantity;
} else {
// IItemManifest
return offer.StoreItem + "x" + offer.QuantityMultiplier;
}
};
let vendorManifestsUsingFullStock = false;
const vendorManifestCache: Record<string, IVendorManifest> = {};
const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => {
const clearVendorCache = (): void => {
for (const k of Object.keys(vendorManifestCache)) {
delete vendorManifestCache[k];
}
};
const generateVendorManifest = (
vendorInfo: IGeneratableVendorInfo,
fullStock: boolean | undefined
): IVendorManifest => {
fullStock ??= config.fullyStockedVendors;
fullStock ??= false;
if (vendorManifestsUsingFullStock != fullStock) {
vendorManifestsUsingFullStock = fullStock;
clearVendorCache();
}
if (!(vendorInfo.TypeName in vendorManifestCache)) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { cycleOffset, cycleDuration, ...clientVendorInfo } = vendorInfo;
@ -244,10 +209,16 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
}
const cacheEntry = vendorManifestCache[vendorInfo.TypeName];
const info = cacheEntry.VendorInfo;
if (Date.now() >= parseInt(info.Expiry.$date.$numberLong)) {
const manifest = ExportVendors[vendorInfo.TypeName];
const cycleDurationRange = getCycleDurationRange(manifest);
let now = Date.now();
if (cycleDurationRange && cycleDurationRange.minValue != cycleDurationRange.maxValue) {
now -= (cycleDurationRange.maxValue - 1) * unixTimesInMs.hour;
}
while (Date.now() >= parseInt(info.Expiry.$date.$numberLong)) {
// Remove expired offers
for (let i = 0; i != info.ItemManifest.length; ) {
if (Date.now() >= parseInt(info.ItemManifest[i].Expiry.$date.$numberLong)) {
if (now >= parseInt(info.ItemManifest[i].Expiry.$date.$numberLong)) {
info.ItemManifest.splice(i, 1);
} else {
++i;
@ -258,62 +229,117 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
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 cycleIndex = Math.trunc((now - cycleOffset) / cycleDuration);
const rng = new SRng(mixSeeds(vendorSeed, cycleIndex));
const manifest = ExportVendors[vendorInfo.TypeName];
const offersToAdd = [];
if (
manifest.numItems &&
(manifest.numItems.minValue != manifest.numItems.maxValue ||
manifest.items.length != manifest.numItems.minValue) &&
!manifest.isOneBinPerCycle
) {
const remainingItemCapacity: Record<string, number> = {};
for (const item of manifest.items) {
remainingItemCapacity[item.storeItem] = 1 + item.duplicates;
}
for (const offer of info.ItemManifest) {
remainingItemCapacity[offer.StoreItem] -= 1;
}
const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue);
while (info.ItemManifest.length + offersToAdd.length < numItemsTarget) {
// TODO: Consider per-bin item limits
// TODO: Consider item probability weightings
const item = rng.randomElement(manifest.items)!;
if (remainingItemCapacity[item.storeItem] != 0) {
remainingItemCapacity[item.storeItem] -= 1;
offersToAdd.push(item);
const offersToAdd: IVendorOffer[] = [];
if (manifest.isOneBinPerCycle) {
if (fullStock) {
for (const rawItem of manifest.items) {
offersToAdd.push(rawItem);
}
} else {
const binThisCycle = cycleIndex % 2; // Note: May want to check the actual number of bins, but this is only used for coda weapons right now.
for (const rawItem of manifest.items) {
if (rawItem.bin == binThisCycle) {
offersToAdd.push(rawItem);
}
}
}
} 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.
// Compute vendor requirements, subtracting existing offers
const remainingItemCapacity: Record<TOfferId, number> = {};
const missingItemsPerBin: Record<number, number> = {};
let numOffersThatNeedToMatchABin = 0;
if (manifest.numItemsPerBin) {
for (let bin = 0; bin != manifest.numItemsPerBin.length; ++bin) {
missingItemsPerBin[bin] = manifest.numItemsPerBin[bin];
numOffersThatNeedToMatchABin += manifest.numItemsPerBin[bin];
}
}
for (const rawItem of manifest.items) {
if (!manifest.isOneBinPerCycle || rawItem.bin == binThisCycle) {
offersToAdd.push(rawItem);
for (const item of manifest.items) {
remainingItemCapacity[getOfferId(item)] = 1 + item.duplicates;
}
for (const offer of info.ItemManifest) {
remainingItemCapacity[getOfferId(offer)] -= 1;
const bin = parseInt(offer.Bin.substring(4));
if (missingItemsPerBin[bin]) {
missingItemsPerBin[bin] -= 1;
numOffersThatNeedToMatchABin -= 1;
}
}
// 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();
// Add permanent offers
let numUncountedOffers = 0;
let numCountedOffers = 0;
let offset = 0;
for (const item of manifest.items) {
if (item.alwaysOffered || item.rotatedWeekly) {
++numUncountedOffers;
const id = getOfferId(item);
if (remainingItemCapacity[id] != 0) {
remainingItemCapacity[id] -= 1;
offersToAdd.push(item);
++offset;
}
} else {
numCountedOffers += 1 + item.duplicates;
}
}
// Add counted offers
const useRng =
manifest.numItems &&
(manifest.numItems.minValue != manifest.numItems.maxValue ||
manifest.numItems.minValue != numCountedOffers);
const numItemsTarget = fullStock
? numUncountedOffers + numCountedOffers
: manifest.numItems
? numUncountedOffers +
(useRng
? rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue)
: manifest.numItems.minValue)
: manifest.items.length;
let i = 0;
const rollableOffers = manifest.items.filter(x => x.probability !== undefined) as (Omit<
IVendorOffer,
"probability"
> & { probability: number })[];
while (info.ItemManifest.length + offersToAdd.length < numItemsTarget) {
const item = useRng ? rng.randomReward(rollableOffers)! : rollableOffers[i++];
if (
remainingItemCapacity[getOfferId(item)] != 0 &&
(numOffersThatNeedToMatchABin == 0 || missingItemsPerBin[item.bin])
) {
remainingItemCapacity[getOfferId(item)] -= 1;
if (missingItemsPerBin[item.bin]) {
missingItemsPerBin[item.bin] -= 1;
numOffersThatNeedToMatchABin -= 1;
}
offersToAdd.splice(offset, 0, item);
}
if (i == rollableOffers.length) {
i = 0;
}
}
}
const cycleStart = cycleOffset + cycleIndex * cycleDuration;
for (const rawItem of offersToAdd) {
const durationHoursRange = toRange(rawItem.durationHours ?? cycleDuration);
const expiry =
cycleStart +
rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour;
const expiry = rawItem.alwaysOffered
? 2051240400_000
: cycleStart +
(rawItem.rotatedWeekly
? unixTimesInMs.week
: rng.randomInt(durationHoursRange.minValue, durationHoursRange.maxValue) * unixTimesInMs.hour);
const item: IItemManifest = {
StoreItem: rawItem.storeItem,
ItemPrices: rawItem.itemPrices?.map(itemPrice => ({ ...itemPrice, ProductCategory: "MiscItems" })),
Bin: "BIN_" + rawItem.bin,
QuantityMultiplier: 1,
QuantityMultiplier: rawItem.quantity,
Expiry: { $date: { $numberLong: expiry.toString() } },
AllowMultipurchase: false,
PurchaseQuantityLimit: rawItem.purchaseLimit,
RotatedWeekly: rawItem.rotatedWeekly,
AllowMultipurchase: rawItem.purchaseLimit !== 1,
Id: {
$oid:
((cycleStart / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
@ -322,7 +348,7 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
}
};
if (rawItem.numRandomItemPrices) {
item.ItemPrices = [];
item.ItemPrices ??= [];
for (let i = 0; i != rawItem.numRandomItemPrices; ++i) {
let itemPrice: { type: string; count: IRange };
do {
@ -362,6 +388,14 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
info.ItemManifest.push(item);
}
if (manifest.numItemsPerBin) {
info.ItemManifest.sort((a, b) => {
const aBin = parseInt(a.Bin.substring(4));
const bBin = parseInt(b.Bin.substring(4));
return aBin == bBin ? 0 : aBin < bBin ? +1 : -1;
});
}
// Update vendor expiry
let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER;
for (const offer of info.ItemManifest) {
@ -371,21 +405,90 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
}
}
info.Expiry.$date.$numberLong = soonestOfferExpiry.toString();
now += unixTimesInMs.hour;
}
return cacheEntry;
};
if (isDev) {
const ads = getVendorManifestByTypeName("/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest")!
if (args.dev) {
if (
getCycleDuration(ExportVendors["/Lotus/Types/Game/VendorManifests/Hubs/TeshinHardModeVendorManifest"]) !=
unixTimesInMs.week
) {
logger.warn(`getCycleDuration self test failed`);
}
for (let i = 0; i != 2; ++i) {
const fullStock = !!i;
const ads = getVendorManifestByTypeName(
"/Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest",
fullStock
)!.VendorInfo.ItemManifest;
if (
ads.length != 5 ||
ads[0].Bin != "BIN_4" ||
ads[1].Bin != "BIN_3" ||
ads[2].Bin != "BIN_2" ||
ads[3].Bin != "BIN_1" ||
ads[4].Bin != "BIN_0"
) {
logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest`);
}
const pall = getVendorManifestByTypeName(
"/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest",
fullStock
)!.VendorInfo.ItemManifest;
if (
pall.length != 5 ||
pall[0].StoreItem != "/Lotus/StoreItems/Types/Items/ShipDecos/HarrowQuestKeyOrnament" ||
pall[1].StoreItem != "/Lotus/StoreItems/Types/BoosterPacks/RivenModPack" ||
pall[2].StoreItem != "/Lotus/StoreItems/Types/StoreItems/CreditBundles/150000Credits" ||
pall[3].StoreItem != "/Lotus/StoreItems/Types/Items/MiscItems/Kuva" ||
pall[4].StoreItem != "/Lotus/StoreItems/Types/BoosterPacks/RivenModPack"
) {
logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest`);
}
}
const cms = getVendorManifestByTypeName(
"/Lotus/Types/Game/VendorManifests/Hubs/RailjackCrewMemberVendorManifest",
false
)!.VendorInfo.ItemManifest;
if (
cms.length != 9 ||
cms[0].Bin != "BIN_2" ||
cms[8].Bin != "BIN_0" ||
cms.reduce((a, x) => a + (x.Bin == "BIN_2" ? 1 : 0), 0) < 2 ||
cms.reduce((a, x) => a + (x.Bin == "BIN_1" ? 1 : 0), 0) < 2 ||
cms.reduce((a, x) => a + (x.Bin == "BIN_0" ? 1 : 0), 0) < 4
) {
logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Hubs/RailjackCrewMemberVendorManifest`);
}
const temple = getVendorManifestByTypeName(
"/Lotus/Types/Game/VendorManifests/TheHex/Temple1999VendorManifest",
false
)!.VendorInfo.ItemManifest;
if (!temple.find(x => x.StoreItem == "/Lotus/StoreItems/Types/Items/MiscItems/Kuva")) {
logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/TheHex/Temple1999VendorManifest`);
}
const nakak = getVendorManifestByTypeName("/Lotus/Types/Game/VendorManifests/Ostron/MaskSalesmanManifest", false)!
.VendorInfo.ItemManifest;
if (
ads.length != 5 ||
ads[0].Bin != "BIN_4" ||
ads[1].Bin != "BIN_3" ||
ads[2].Bin != "BIN_2" ||
ads[3].Bin != "BIN_1" ||
ads[4].Bin != "BIN_0"
nakak.length != 10 ||
nakak[0].StoreItem != "/Lotus/StoreItems/Upgrades/Skins/Ostron/RevenantMask" ||
nakak[1].StoreItem != "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyThumper" ||
nakak[1].ItemPrices?.length != 4 ||
nakak[2].StoreItem != "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyThumperMedium" ||
nakak[2].ItemPrices?.length != 4 ||
nakak[3].StoreItem != "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyThumperLarge" ||
nakak[3].ItemPrices?.length != 4
// The remaining offers should be computed by weighted RNG.
) {
logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Hubs/GuildAdvertisementVendorManifest`);
logger.warn(`self test failed for /Lotus/Types/Game/VendorManifests/Ostron/MaskSalesmanManifest`);
}
}

View File

@ -5,9 +5,17 @@ import { config } from "./configService";
import { logger } from "../utils/logger";
import { app } from "../app";
import { AddressInfo } from "node:net";
import ws from "ws";
import { Account } from "../models/loginModel";
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "./loginService";
import { IDatabaseAccountJson } from "../types/loginTypes";
import { HydratedDocument } from "mongoose";
import { Agent, WebSocket as UnidiciWebSocket } from "undici";
let httpServer: http.Server | undefined;
let httpsServer: https.Server | undefined;
let wsServer: ws.Server | undefined;
let wssServer: ws.Server | undefined;
const tlsOptions = {
key: fs.readFileSync("static/certs/key.pem"),
@ -21,19 +29,65 @@ export const startWebServer = (): void => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
httpServer = http.createServer(app);
httpServer.listen(httpPort, () => {
wsServer = new ws.Server({ server: httpServer });
wsServer.on("connection", wsOnConnect);
logger.info("HTTP server started on port " + httpPort);
// eslint-disable-next-line @typescript-eslint/no-misused-promises
httpsServer = https.createServer(tlsOptions, app);
httpsServer.listen(httpsPort, () => {
wssServer = new ws.Server({ server: httpsServer });
wssServer.on("connection", wsOnConnect);
logger.info("HTTPS server started on port " + httpsPort);
logger.info(
"Access the WebUI in your browser at http://localhost" + (httpPort == 80 ? "" : ":" + httpPort)
);
void runWsSelfTest("wss", httpsPort).then(ok => {
if (!ok) {
logger.warn(`WSS self-test failed. The server may not actually be reachable at port ${httpsPort}.`);
if (process.platform == "win32") {
logger.warn(
`You can check who actually has that port via powershell: Get-Process -Id (Get-NetTCPConnection -LocalPort ${httpsPort}).OwningProcess`
);
}
}
});
});
});
};
const runWsSelfTest = (protocol: "ws" | "wss", port: number): Promise<boolean> => {
return new Promise(resolve => {
// https://github.com/oven-sh/bun/issues/20547
if (process.versions.bun) {
const client = new WebSocket(`${protocol}://localhost:${port}/custom/selftest`, {
tls: { rejectUnauthorized: false }
} as unknown as string);
client.onmessage = (e): void => {
resolve(e.data == "SpaceNinjaServer");
};
client.onerror = client.onclose = (): void => {
resolve(false);
};
} else {
const agent = new Agent({ connect: { rejectUnauthorized: false } });
const client = new UnidiciWebSocket(`${protocol}://localhost:${port}/custom/selftest`, {
dispatcher: agent
});
client.onmessage = (e): void => {
resolve(e.data == "SpaceNinjaServer");
};
client.onerror = client.onclose = (): void => {
resolve(false);
};
}
});
};
export const getWebPorts = (): Record<"http" | "https", number | undefined> => {
return {
http: (httpServer?.address() as AddressInfo | undefined)?.port,
@ -61,5 +115,156 @@ export const stopWebServer = async (): Promise<void> => {
})
);
}
if (wsServer) {
promises.push(
new Promise(resolve => {
wsServer!.close(() => {
resolve();
});
})
);
}
if (wssServer) {
promises.push(
new Promise(resolve => {
wssServer!.close(() => {
resolve();
});
})
);
}
await Promise.all(promises);
};
interface IWsCustomData extends ws {
accountId?: string;
}
interface IWsMsgFromClient {
auth?: {
email: string;
password: string;
isRegister: boolean;
};
logout?: boolean;
}
interface IWsMsgToClient {
reload?: boolean;
ports?: {
http: number | undefined;
https: number | undefined;
};
config_reloaded?: boolean;
auth_succ?: {
id: string;
DisplayName: string;
Nonce: number;
};
auth_fail?: {
isRegister: boolean;
};
logged_out?: boolean;
update_inventory?: boolean;
}
const wsOnConnect = (ws: ws, req: http.IncomingMessage): void => {
if (req.url == "/custom/selftest") {
ws.send("SpaceNinjaServer");
ws.close();
return;
}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
ws.on("message", async msg => {
const data = JSON.parse(String(msg)) as IWsMsgFromClient;
if (data.auth) {
let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email });
if (account) {
if (isCorrectPassword(data.auth.password, account.password)) {
if (!account.Nonce) {
account.ClientType = "webui";
account.Nonce = createNonce();
await (account as HydratedDocument<IDatabaseAccountJson>).save();
}
} else {
account = null;
}
} else if (data.auth.isRegister) {
const name = await getUsernameFromEmail(data.auth.email);
account = await createAccount({
email: data.auth.email,
password: data.auth.password,
ClientType: "webui",
LastLogin: new Date(),
DisplayName: name,
Nonce: createNonce()
});
}
if (account) {
(ws as IWsCustomData).accountId = account.id;
ws.send(
JSON.stringify({
auth_succ: {
id: account.id,
DisplayName: account.DisplayName,
Nonce: account.Nonce
}
} satisfies IWsMsgToClient)
);
} else {
ws.send(
JSON.stringify({
auth_fail: {
isRegister: data.auth.isRegister
}
} satisfies IWsMsgToClient)
);
}
}
if (data.logout) {
const accountId = (ws as IWsCustomData).accountId;
(ws as IWsCustomData).accountId = undefined;
await Account.updateOne(
{
_id: accountId,
ClientType: "webui"
},
{
Nonce: 0
}
);
}
});
};
export const sendWsBroadcast = (data: IWsMsgToClient): void => {
const msg = JSON.stringify(data);
if (wsServer) {
for (const client of wsServer.clients) {
client.send(msg);
}
}
if (wssServer) {
for (const client of wssServer.clients) {
client.send(msg);
}
}
};
export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void => {
const msg = JSON.stringify(data);
if (wsServer) {
for (const client of wsServer.clients) {
if ((client as IWsCustomData).accountId == accountId) {
client.send(msg);
}
}
}
if (wssServer) {
for (const client of wssServer.clients) {
if ((client as IWsCustomData).accountId == accountId) {
client.send(msg);
}
}
}
};

View File

@ -1,12 +1,13 @@
import staticWorldState from "@/static/fixed_responses/worldState/worldState.json";
import fissureMissions from "@/static/fixed_responses/worldState/fissureMissions.json";
import sortieTilesets from "@/static/fixed_responses/worldState/sortieTilesets.json";
import sortieTilesetMissions from "@/static/fixed_responses/worldState/sortieTilesetMissions.json";
import syndicateMissions from "@/static/fixed_responses/worldState/syndicateMissions.json";
import { buildConfig } from "@/src/services/buildConfigService";
import { unixTimesInMs } from "@/src/constants/timeConstants";
import { config } from "@/src/services/configService";
import { SRng } from "@/src/services/rngService";
import { ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
import { getRandomElement, getRandomInt, SRng } from "@/src/services/rngService";
import { eMissionType, ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
import {
ICalendarDay,
ICalendarEvent,
@ -16,10 +17,14 @@ import {
ISortie,
ISortieMission,
ISyndicateMissionInfo,
IWorldState
ITmp,
IVoidStorm,
IWorldState,
TCircuitGameMode
} from "../types/worldStateTypes";
import { version_compare } from "../helpers/inventoryHelpers";
import { toMongoDate, toOid, version_compare } from "../helpers/inventoryHelpers";
import { logger } from "../utils/logger";
import { Fissure } from "../models/worldStateModel";
const sortieBosses = [
"SORTIE_BOSS_HYENA",
@ -453,13 +458,37 @@ const pushWeeklyActs = (
}
};
const generateXpAmounts = (rng: SRng, stageCount: number, minXp: number, maxXp: number): number[] => {
const step = minXp < 1000 ? 1 : 10;
const totalDeciXp = rng.randomInt(minXp / step, maxXp / step);
const xpAmounts: number[] = [];
if (stageCount < 4) {
const perStage = Math.ceil(totalDeciXp / stageCount) * step;
for (let i = 0; i != stageCount; ++i) {
xpAmounts.push(perStage);
}
} else {
const perStage = Math.ceil(Math.round(totalDeciXp * 0.667) / (stageCount - 1)) * step;
for (let i = 0; i != stageCount - 1; ++i) {
xpAmounts.push(perStage);
}
xpAmounts.push(Math.ceil(totalDeciXp * 0.332) * step);
}
return xpAmounts;
};
// Test vectors:
//console.log(generateXpAmounts(new SRng(1337n), 5, 5000, 5000)); // [840, 840, 840, 840, 1660]
//console.log(generateXpAmounts(new SRng(1337n), 3, 40, 40)); // [14, 14, 14]
//console.log(generateXpAmounts(new SRng(1337n), 5, 150, 150)); // [25, 25, 25, 25, 50]
//console.log(generateXpAmounts(new SRng(1337n), 4, 10, 10)); // [2, 2, 2, 4]
//console.log(generateXpAmounts(new SRng(1337n), 4, 15, 15)); // [4, 4, 4, 5]
//console.log(generateXpAmounts(new SRng(1337n), 4, 20, 20)); // [5, 5, 5, 7]
export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[], bountyCycle: number): void => {
const table = String.fromCharCode(65 + (bountyCycle % 3));
const vaultTable = String.fromCharCode(65 + ((bountyCycle + 1) % 3));
const deimosDTable = String.fromCharCode(65 + (bountyCycle % 2));
// TODO: xpAmounts need to be calculated based on the jobType somehow?
const seed = new SRng(bountyCycle).randomInt(0, 100_000);
const bountyCycleStart = bountyCycle * 9000000;
const bountyCycleEnd = bountyCycleStart + 9000000;
@ -482,7 +511,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 0,
minEnemyLevel: 5,
maxEnemyLevel: 15,
xpAmounts: [430, 430, 430]
xpAmounts: generateXpAmounts(rng, 3, 1000, 1500)
},
{
jobType: rng.randomElement(eidolonJobs),
@ -490,7 +519,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 1,
minEnemyLevel: 10,
maxEnemyLevel: 30,
xpAmounts: [620, 620, 620]
xpAmounts: generateXpAmounts(rng, 3, 1750, 2250)
},
{
jobType: rng.randomElement(eidolonJobs),
@ -498,7 +527,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 2,
minEnemyLevel: 20,
maxEnemyLevel: 40,
xpAmounts: [670, 670, 670, 990]
xpAmounts: generateXpAmounts(rng, 4, 2500, 3000)
},
{
jobType: rng.randomElement(eidolonJobs),
@ -506,7 +535,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 3,
minEnemyLevel: 30,
maxEnemyLevel: 50,
xpAmounts: [570, 570, 570, 570, 1110]
xpAmounts: generateXpAmounts(rng, 5, 3250, 3750)
},
{
jobType: rng.randomElement(eidolonJobs),
@ -514,7 +543,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 5,
minEnemyLevel: 40,
maxEnemyLevel: 60,
xpAmounts: [740, 740, 740, 740, 1450]
xpAmounts: generateXpAmounts(rng, 5, 4000, 4500)
},
{
jobType: rng.randomElement(eidolonJobs),
@ -530,7 +559,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 0,
minEnemyLevel: 50,
maxEnemyLevel: 70,
xpAmounts: [840, 840, 840, 840, 1650]
xpAmounts: generateXpAmounts(rng, 5, 4500, 5000)
}
]
});
@ -554,7 +583,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 0,
minEnemyLevel: 5,
maxEnemyLevel: 15,
xpAmounts: [340, 340, 340]
xpAmounts: generateXpAmounts(rng, 3, 1000, 1500)
},
{
jobType: rng.randomElement(venusJobs),
@ -562,7 +591,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 1,
minEnemyLevel: 10,
maxEnemyLevel: 30,
xpAmounts: [660, 660, 660]
xpAmounts: generateXpAmounts(rng, 3, 1750, 2250)
},
{
jobType: rng.randomElement(venusJobs),
@ -570,7 +599,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 2,
minEnemyLevel: 20,
maxEnemyLevel: 40,
xpAmounts: [610, 610, 610, 900]
xpAmounts: generateXpAmounts(rng, 4, 2500, 3000)
},
{
jobType: rng.randomElement(venusJobs),
@ -578,7 +607,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 3,
minEnemyLevel: 30,
maxEnemyLevel: 50,
xpAmounts: [600, 600, 600, 600, 1170]
xpAmounts: generateXpAmounts(rng, 5, 3250, 3750)
},
{
jobType: rng.randomElement(venusJobs),
@ -586,7 +615,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 5,
minEnemyLevel: 40,
maxEnemyLevel: 60,
xpAmounts: [690, 690, 690, 690, 1350]
xpAmounts: generateXpAmounts(rng, 5, 4000, 4500)
},
{
jobType: rng.randomElement(venusJobs),
@ -602,7 +631,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 0,
minEnemyLevel: 50,
maxEnemyLevel: 70,
xpAmounts: [780, 780, 780, 780, 1540]
xpAmounts: generateXpAmounts(rng, 5, 4500, 5000)
}
]
});
@ -626,7 +655,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 0,
minEnemyLevel: 5,
maxEnemyLevel: 15,
xpAmounts: [5, 5, 5]
xpAmounts: generateXpAmounts(rng, 3, 12, 18)
},
{
jobType: rng.randomElement(microplanetJobs),
@ -634,7 +663,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 1,
minEnemyLevel: 15,
maxEnemyLevel: 25,
xpAmounts: [12, 12, 12]
xpAmounts: generateXpAmounts(rng, 3, 24, 36)
},
{
jobType: rng.randomElement(microplanetEndlessJobs),
@ -651,7 +680,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 2,
minEnemyLevel: 30,
maxEnemyLevel: 40,
xpAmounts: [17, 17, 17, 25]
xpAmounts: generateXpAmounts(rng, 4, 72, 88)
},
{
jobType: rng.randomElement(microplanetJobs),
@ -659,7 +688,7 @@ export const pushClassicBounties = (syndicateMissions: ISyndicateMissionInfo[],
masteryReq: 3,
minEnemyLevel: 40,
maxEnemyLevel: 60,
xpAmounts: [22, 22, 22, 22, 43]
xpAmounts: generateXpAmounts(rng, 5, 115, 135)
},
{
jobType: rng.randomElement(microplanetJobs),
@ -939,6 +968,61 @@ const getCalendarSeason = (week: number): ICalendarSeason => {
};
};
// Not very faithful, but to avoid the same node coming up back-to-back (which is not valid), I've split these into 2 arrays which we're alternating between.
const voidStormMissionsA = {
VoidT1: ["CrewBattleNode519", "CrewBattleNode518", "CrewBattleNode515", "CrewBattleNode503"],
VoidT2: ["CrewBattleNode501", "CrewBattleNode534", "CrewBattleNode530"],
VoidT3: ["CrewBattleNode521", "CrewBattleNode516"],
VoidT4: [
"CrewBattleNode555",
"CrewBattleNode553",
"CrewBattleNode554",
"CrewBattleNode539",
"CrewBattleNode531",
"CrewBattleNode527"
]
};
const voidStormMissionsB = {
VoidT1: ["CrewBattleNode509", "CrewBattleNode522", "CrewBattleNode511", "CrewBattleNode512"],
VoidT2: ["CrewBattleNode535", "CrewBattleNode533"],
VoidT3: ["CrewBattleNode524", "CrewBattleNode525"],
VoidT4: [
"CrewBattleNode542",
"CrewBattleNode538",
"CrewBattleNode543",
"CrewBattleNode536",
"CrewBattleNode550",
"CrewBattleNode529"
]
};
const pushVoidStorms = (arr: IVoidStorm[], hour: number): void => {
const activation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute;
const expiry = activation + 90 * unixTimesInMs.minute;
let accum = 0;
const rng = new SRng(new SRng(hour).randomInt(0, 100_000));
const voidStormMissions = structuredClone(hour & 1 ? voidStormMissionsA : voidStormMissionsB);
for (const tier of ["VoidT1", "VoidT1", "VoidT2", "VoidT3", "VoidT4", "VoidT4"] as const) {
const idx = rng.randomInt(0, voidStormMissions[tier].length - 1);
const node = voidStormMissions[tier][idx];
voidStormMissions[tier].splice(idx, 1);
arr.push({
_id: {
$oid:
((activation / 1000) & 0xffffffff).toString(16).padStart(8, "0") +
"0321e89b" +
(accum++).toString().padStart(8, "0")
},
Node: node,
Activation: { $date: { $numberLong: activation.toString() } },
Expiry: { $date: { $numberLong: expiry.toString() } },
ActiveMissionTier: tier
});
}
};
const doesTimeSatsifyConstraints = (timeSecs: number): boolean => {
if (config.worldState?.eidolonOverride) {
const eidolonEpoch = 1391992660;
@ -986,6 +1070,27 @@ const doesTimeSatsifyConstraints = (timeSecs: number): boolean => {
}
}
if (config.worldState?.duviriOverride) {
const duviriMoods = ["sorrow", "fear", "joy", "anger", "envy"];
const desiredMood = duviriMoods.indexOf(config.worldState.duviriOverride);
if (desiredMood == -1) {
logger.warn(`ignoring invalid config value for worldState.duviriOverride`, {
value: config.worldState.duviriOverride,
valid_values: duviriMoods
});
} else {
const moodIndex = Math.trunc(timeSecs / 7200);
const moodStart = moodIndex * 7200;
const moodEnd = moodStart + 7200;
if (
moodIndex % 5 != desiredMood ||
isBeforeNextExpectedWorldStateRefresh(timeSecs * 1000, moodEnd * 1000)
) {
return false;
}
}
}
return true;
};
@ -1007,20 +1112,18 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
Alerts: [],
Sorties: [],
LiteSorties: [],
ActiveMissions: [],
GlobalUpgrades: [],
VoidStorms: [],
EndlessXpChoices: [],
KnownCalendarSeasons: [],
...staticWorldState,
SyndicateMissions: [...staticWorldState.SyndicateMissions]
};
// Omit void fissures for versions prior to Dante Unbound to avoid script errors.
if (buildLabel && version_compare(buildLabel, "2024.03.24.20.00") < 0) {
worldState.ActiveMissions = [];
if (version_compare(buildLabel, "2017.10.12.17.04") < 0) {
// Old versions seem to really get hung up on not being able to load these.
worldState.PVPChallengeInstances = [];
}
// Old versions seem to really get hung up on not being able to load these.
if (buildLabel && version_compare(buildLabel, "2017.10.12.17.04") < 0) {
worldState.PVPChallengeInstances = [];
}
if (config.worldState?.starDays) {
@ -1204,12 +1307,38 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
worldState.KnownCalendarSeasons.push(getCalendarSeason(week + 1));
}
// Sentient Anomaly cycling every 30 minutes
// Void Storms
const hour = Math.trunc(timeMs / unixTimesInMs.hour);
const overLastHourStormExpiry = hour * unixTimesInMs.hour + 10 * unixTimesInMs.minute;
const thisHourStormActivation = hour * unixTimesInMs.hour + 40 * unixTimesInMs.minute;
if (overLastHourStormExpiry > timeMs) {
pushVoidStorms(worldState.VoidStorms, hour - 2);
}
pushVoidStorms(worldState.VoidStorms, hour - 1);
if (isBeforeNextExpectedWorldStateRefresh(timeMs, thisHourStormActivation)) {
pushVoidStorms(worldState.VoidStorms, hour);
}
// Sentient Anomaly + Xtra Cheese cycles
const halfHour = Math.trunc(timeMs / (unixTimesInMs.hour / 2));
const tmp = {
const hourInSeconds = 3600;
const cheeseInterval = hourInSeconds * 8;
const cheeseDuration = hourInSeconds * 2;
const cheeseIndex = Math.trunc(timeSecs / cheeseInterval);
let cheeseStart = cheeseIndex * cheeseInterval;
let cheeseEnd = cheeseStart + cheeseDuration;
let cheeseNext = (cheeseIndex + 1) * cheeseInterval;
// Live servers only update the start time once it happens, which makes the
// client show a negative countdown during off-hours. Optionally adjust the
// times so the next activation is always in the future.
if (config.unfaithfulBugFixes?.fixXtraCheeseTimer && timeSecs >= cheeseEnd) {
cheeseStart = cheeseNext;
cheeseEnd = cheeseStart + cheeseDuration;
cheeseNext += cheeseInterval;
}
const tmp: ITmp = {
cavabegin: "1690761600",
PurchasePlatformLockEnabled: true,
tcsn: true,
pgr: {
ts: "1732572900",
en: "CUSTOM DECALS @ ZEVILA",
@ -1230,13 +1359,39 @@ export const getWorldState = (buildLabel?: string): IWorldState => {
},
ennnd: true,
mbrt: true,
fbst: {
a: cheeseStart,
e: cheeseEnd,
n: cheeseNext
},
sfn: [550, 553, 554, 555][halfHour % 4]
};
if (Array.isArray(config.worldState?.circuitGameModes)) {
tmp.edg = config.worldState.circuitGameModes as TCircuitGameMode[];
}
worldState.Tmp = JSON.stringify(tmp);
return worldState;
};
export const populateFissures = async (worldState: IWorldState): Promise<void> => {
const fissures = await Fissure.find({});
for (const fissure of fissures) {
const meta = ExportRegions[fissure.Node];
worldState.ActiveMissions.push({
_id: toOid(fissure._id),
Region: meta.systemIndex + 1,
Seed: 1337,
Activation: toMongoDate(fissure.Activation),
Expiry: toMongoDate(fissure.Expiry),
Node: fissure.Node,
MissionType: eMissionType[meta.missionIndex].tag,
Modifier: fissure.Modifier,
Hard: fissure.Hard
});
}
};
export const idToBountyCycle = (id: string): number => {
return Math.trunc((parseInt(id.substring(0, 8), 16) * 1000) / 9000_000);
};
@ -1364,3 +1519,57 @@ const nightwaveTagToSeason: Record<string, number> = {
RadioLegionIntermissionSyndicate: 1, // Intermission I
RadioLegionSyndicate: 0 // The Wolf of Saturn Six
};
export const updateWorldStateCollections = async (): Promise<void> => {
const fissures = await Fissure.find();
const activeNodes = new Set<string>();
const tierToFurthestExpiry: Record<string, number> = {
VoidT1: 0,
VoidT2: 0,
VoidT3: 0,
VoidT4: 0,
VoidT5: 0,
VoidT6: 0,
VoidT1Hard: 0,
VoidT2Hard: 0,
VoidT3Hard: 0,
VoidT4Hard: 0,
VoidT5Hard: 0,
VoidT6Hard: 0
};
for (const fissure of fissures) {
activeNodes.add(fissure.Node);
const key = fissure.Modifier + (fissure.Hard ? "Hard" : "");
tierToFurthestExpiry[key] = Math.max(tierToFurthestExpiry[key], fissure.Expiry.getTime());
}
const deadline = Date.now() - 6 * unixTimesInMs.minute;
for (const [tier, expiry] of Object.entries(tierToFurthestExpiry)) {
if (expiry < deadline) {
const numFissures = getRandomInt(1, 3);
for (let i = 0; i != numFissures; ++i) {
const modifier = tier.replace("Hard", "") as
| "VoidT1"
| "VoidT2"
| "VoidT3"
| "VoidT4"
| "VoidT5"
| "VoidT6";
let node: string;
do {
node = getRandomElement(fissureMissions[modifier])!;
} while (activeNodes.has(node));
activeNodes.add(node);
await Fissure.insertOne({
Activation: new Date(),
Expiry: new Date(Date.now() + getRandomInt(60, 120) * unixTimesInMs.minute),
Node: node,
Modifier: modifier,
Hard: tier.indexOf("Hard") != -1 ? true : undefined
});
}
}
}
};

View File

@ -234,7 +234,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu
HandlerPoints: number;
MiscItems: IMiscItem[];
HasOwnedVoidProjectionsPreviously?: boolean;
ChallengesFixVersion: number;
ChallengesFixVersion?: number;
ChallengeProgress: IChallengeProgress[];
RawUpgrades: IRawUpgrade[];
ReceivedStartingGear: boolean;
@ -641,11 +641,11 @@ export interface IFocusUpgrade {
}
export interface IFocusXP {
AP_POWER: number;
AP_TACTIC: number;
AP_DEFENSE: number;
AP_ATTACK: number;
AP_WARD: number;
AP_POWER?: number;
AP_TACTIC?: number;
AP_DEFENSE?: number;
AP_ATTACK?: number;
AP_WARD?: number;
}
export type TFocusPolarity = keyof IFocusXP;

View File

@ -2,7 +2,7 @@ import { Types } from "mongoose";
export interface IAccountAndLoginResponseCommons {
DisplayName: string;
CountryCode: string;
CountryCode?: string;
ClientType?: string;
CrossPlatformAllowed?: boolean;
ForceLogoutVersion?: number;

View File

@ -10,14 +10,42 @@ import {
ICrewMemberClient
} from "./inventoryTypes/inventoryTypes";
export enum PurchaseSource {
Market = 0,
VoidTrader = 1,
SyndicateFavor = 2,
DailyDeal = 3,
Arsenal = 4,
Profile = 5,
Hub = 6,
Vendor = 7,
AppearancePreview = 8,
Museum = 9,
Operator = 10,
PlayerShip = 11,
Crewship = 12,
MenuStyle = 13,
MenuHud = 14,
Chat = 15,
Inventory = 16,
StarChart = 17,
PrimeVaultTrader = 18,
Incubator = 19,
Prompt = 20,
Kaithe = 21,
DuviriWeapon = 22,
UpdateScreen = 23,
Motorcycle = 24
}
export interface IPurchaseRequest {
PurchaseParams: IPurchaseParams;
buildLabel: string;
}
export interface IPurchaseParams {
Source: number;
SourceId?: string; // for Source 1, 7 & 18
Source: PurchaseSource;
SourceId?: string; // VoidTrader, Vendor, PrimeVaultTrader
StoreItem: string;
StorePage: string;
SearchTerm: string;
@ -25,10 +53,10 @@ export interface IPurchaseParams {
Quantity: number;
UsePremium: boolean;
ExpectedPrice: number;
SyndicateTag?: string; // for Source 2
UseFreeFavor?: boolean; // for Source 2
ExtraPurchaseInfoJson?: string; // for Source 7
IsWeekly?: boolean; // for Source 7
SyndicateTag?: string; // SyndicateFavor
UseFreeFavor?: boolean; // SyndicateFavor
ExtraPurchaseInfoJson?: string; // Vendor
IsWeekly?: boolean; // Vendor
}
export type IInventoryChanges = {

View File

@ -9,9 +9,10 @@ export interface IWorldState {
Sorties: ISortie[];
LiteSorties: ILiteSortie[];
SyndicateMissions: ISyndicateMissionInfo[];
GlobalUpgrades: IGlobalUpgrade[];
ActiveMissions: IFissure[];
GlobalUpgrades: IGlobalUpgrade[];
NodeOverrides: INodeOverride[];
VoidStorms: IVoidStorm[];
PVPChallengeInstances: IPVPChallengeInstance[];
EndlessXpChoices: IEndlessXpChoice[];
SeasonInfo?: {
@ -85,6 +86,14 @@ export interface IFissure {
Hard?: boolean;
}
export interface IFissureDatabase {
Activation: Date;
Expiry: Date;
Node: string;
Modifier: "VoidT1" | "VoidT2" | "VoidT3" | "VoidT4" | "VoidT5" | "VoidT6";
Hard?: boolean;
}
export interface INodeOverride {
_id: IOid;
Activation?: IMongoDate;
@ -131,6 +140,14 @@ export interface ILiteSortie {
}[];
}
export interface IVoidStorm {
_id: IOid;
Node: string;
Activation: IMongoDate;
Expiry: IMongoDate;
ActiveMissionTier: string;
}
export interface IPVPChallengeInstance {
_id: IOid;
challengeTypeRefID: string;
@ -182,3 +199,48 @@ export interface ICalendarEvent {
dialogueName?: string;
dialogueConvo?: string;
}
export type TCircuitGameMode =
| "Survival"
| "VoidFlood"
| "Excavation"
| "Defense"
| "Exterminate"
| "Assassination"
| "Alchemy";
export interface ITmp {
cavabegin: string;
PurchasePlatformLockEnabled: boolean; // Seems unused
pgr: IPgr;
ennnd?: boolean; // True if 1999 demo is available (no effect for >=38.6.0)
mbrt?: boolean; // Related to mobile app rating request
fbst: IFbst;
sfn: number;
edg?: TCircuitGameMode[]; // The Circuit game modes overwrite
}
interface IPgr {
ts: string;
en: string;
fr: string;
it: string;
de: string;
es: string;
pt: string;
ru: string;
pl: string;
uk: string;
tr: string;
ja: string;
zh: string;
ko: string;
tc: string;
th: string;
}
interface IFbst {
a: number;
e: number;
n: number;
}

View File

@ -1,38 +0,0 @@
{
"VendorInfo": {
"_id": { "$oid": "62695b0467e5d379750f9f75" },
"TypeName": "/Lotus/Types/Game/VendorManifests/Zariman/ArchimedeanVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/VoidPlumeAOrnament",
"ItemPrices": [{ "ItemCount": 1, "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidAngelItem", "ProductCategory": "MiscItems" }],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "9999999000000" } },
"AllowMultipurchase": true,
"Id": { "$oid": "63ed01ef4c37f93d0b797826" }
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/VoidPlumeBOrnament",
"ItemPrices": [{ "ItemCount": 1, "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidAngelItem", "ProductCategory": "MiscItems" }],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "9999999000000" } },
"AllowMultipurchase": true,
"Id": { "$oid": "63ed01ef4c37f93d0b797827" }
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
"ItemPrices": [{ "ItemCount": 5, "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidAngelItem", "ProductCategory": "MiscItems" }],
"Bin": "BIN_0",
"QuantityMultiplier": 35000,
"Expiry": { "$date": { "$numberLong": "9999999000000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": { "$oid": "66664112af1177b5070ab882" }
}
],
"PropertyTextHash": "DB7BF03C3FE6D0036A4DC30066A9A17E",
"Expiry": { "$date": { "$numberLong": "9999999000000" } }
}
}

View File

@ -1,300 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e01c96976e97d6b802e"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/EntratiFragmentVendorProductsManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/SeriglassShard",
"ItemPrices": [
{
"ItemCount": 20,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e8390"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileDeimosBouncy",
"ItemPrices": [
{
"ItemCount": 50,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e8391"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileDeimosBreakthrough",
"ItemPrices": [
{
"ItemCount": 50,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e8392"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileDeimosCatacombs",
"ItemPrices": [
{
"ItemCount": 50,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e8393"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileDeimosDownfall",
"ItemPrices": [
{
"ItemCount": 50,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e8394"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileDeimosObsession",
"ItemPrices": [
{
"ItemCount": 50,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e8395"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileDeimosTunnels",
"ItemPrices": [
{
"ItemCount": 50,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e8396"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Deimos/FatherTokenShipDeco",
"ItemPrices": [
{
"ItemCount": 10,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonA",
"ProductCategory": "MiscItems"
},
{
"ItemCount": 5,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e83f1"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Deimos/LisetPropEntratiLamp",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonB",
"ItemCount": 12,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e83f2"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Deimos/LisetPropInfestedCrate",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonA",
"ItemCount": 11,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e83f3"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Deimos/LisetPropInfestedCystC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e83f4"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/RequiemRisGlyph",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b10ba592c4c95e83f5"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Deimos/MotherTokenShipDeco",
"ItemPrices": [
{
"ItemCount": 10,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB",
"ProductCategory": "MiscItems"
},
{
"ItemCount": 5,
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentRareA",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e83f6"
}
}
],
"PropertyTextHash": "DB953EE163A65B3BCC0552902321D791",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,241 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e01c96976e97d6b8009"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/HivemindCommisionsManifestFishmonger",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Daughter/DaughterTaskC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishFPartItem",
"ItemCount": 6,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 16,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 63978959,
"Id": {
"$oid": "66fd60b10ba592c4c95e82cc"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Daughter/DaughterTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishBPartItem",
"ItemCount": 5,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishDPartItem",
"ItemCount": 4,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3808064409,
"Id": {
"$oid": "66fd60b10ba592c4c95e82cd"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Daughter/DaughterTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishEPartItem",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 2,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3849710569,
"Id": {
"$oid": "66fd60b10ba592c4c95e82d0"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Daughter/DaughterTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosGenericInfestedFishPartItem",
"ItemCount": 5,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 4,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1687111317,
"Id": {
"$oid": "66fd60b10ba592c4c95e82d1"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Daughter/DaughterTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishAPartItem",
"ItemCount": 4,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishBPartItem",
"ItemCount": 4,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2267414276,
"Id": {
"$oid": "66fd60b10ba592c4c95e82d2"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Daughter/DaughterTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosGenericInfestedFishPartItem",
"ItemCount": 6,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosGenericSharedFishPartItem",
"ItemCount": 6,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1497494256,
"Id": {
"$oid": "66fd60b10ba592c4c95e82d3"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Daughter/DaughterTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosGenericInfestedFishPartItem",
"ItemCount": 5,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 2,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2883527039,
"Id": {
"$oid": "66fd60b10ba592c4c95e82d4"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Daughter/DaughterTaskC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishGPartItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 12,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 4116691539,
"Id": {
"$oid": "66fd60b10ba592c4c95e82d5"
}
}
],
"PropertyTextHash": "54B6992C6314367F8EEA74B7F1A1C352",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,287 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e03c96976e97d6b80a3"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/HivemindCommisionsManifestPetVendor",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMaggotRare",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMaggotCommon",
"ItemCount": 4,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2707699975,
"Id": {
"$oid": "66fd60b10ba592c4c95e8897"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorCommon",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMaggotCommon",
"ItemCount": 4,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3610714639,
"Id": {
"$oid": "66fd60b10ba592c4c95e8898"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorCommon",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMaggotRare",
"ItemCount": 1,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1782149988,
"Id": {
"$oid": "66fd60b10ba592c4c95e8899"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMergooCommon",
"ItemCount": 3,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2149416825,
"Id": {
"$oid": "66fd60b10ba592c4c95e889a"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedCritterRare",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedKdriveUncommon",
"ItemCount": 2,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 890863265,
"Id": {
"$oid": "66fd60b10ba592c4c95e889b"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedCritterCommon",
"ItemCount": 4,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorCommon",
"ItemCount": 4,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2507606934,
"Id": {
"$oid": "66fd60b10ba592c4c95e889c"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorRare",
"ItemCount": 5,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1037784729,
"Id": {
"$oid": "66fd60b10ba592c4c95e889e"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMaggotCommon",
"ItemCount": 4,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedCritterCommon",
"ItemCount": 4,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2048707501,
"Id": {
"$oid": "66fd60b10ba592c4c95e889f"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedKdriveRare",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorCommon",
"ItemCount": 2,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 4038149313,
"Id": {
"$oid": "66fd60b10ba592c4c95e88a0"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Son/SonTaskD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedMergooCommon",
"ItemCount": 3,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/AnimalTagInfestedPredatorRare",
"ItemCount": 3,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2155290001,
"Id": {
"$oid": "66fd60b10ba592c4c95e88a1"
}
}
],
"PropertyTextHash": "61E66B4E9E5A121DD06A476AE2A81B24",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,312 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e01c96976e97d6b7ff1"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/HivemindCommisionsManifestProspector",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreAItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemBItem",
"ItemCount": 8,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1370972414,
"Id": {
"$oid": "66fd60b20ba592c4c95e8ef8"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreBItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosUncommonGemAItem",
"ItemCount": 8,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosUncommonOreAItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2361790143,
"Id": {
"$oid": "66fd60b20ba592c4c95e8ef9"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemAItem",
"ItemCount": 6,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreAItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 472210739,
"Id": {
"$oid": "66fd60b20ba592c4c95e8efb"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreBItem",
"ItemCount": 12,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreAItem",
"ItemCount": 15,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3072462886,
"Id": {
"$oid": "66fd60b20ba592c4c95e8efd"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemAItem",
"ItemCount": 8,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreAItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 767765909,
"Id": {
"$oid": "66fd60b20ba592c4c95e8efe"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosEidolonGemAItem",
"ItemCount": 5,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosEidolonGemBItem",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosRareOreAItem",
"ItemCount": 22,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 512512880,
"Id": {
"$oid": "66fd60b20ba592c4c95e8eff"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreAItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemAItem",
"ItemCount": 6,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2037734419,
"Id": {
"$oid": "66fd60b20ba592c4c95e8f00"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosUncommonOreAItem",
"ItemCount": 13,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreAItem",
"ItemCount": 8,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1433662587,
"Id": {
"$oid": "66fd60b20ba592c4c95e8f01"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonOreAItem",
"ItemCount": 12,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemAItem",
"ItemCount": 8,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1618492734,
"Id": {
"$oid": "66fd60b20ba592c4c95e8f02"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Otak/OtakTaskD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosUncommonGemAItem",
"ItemCount": 7,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemBItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosRareOreAItem",
"ItemCount": 12,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 4032699594,
"Id": {
"$oid": "66fd60b20ba592c4c95e8f03"
}
}
],
"PropertyTextHash": "0AC3C284471037011B36EC51238D13A9",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,223 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e03c96976e97d6b80d2"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/HivemindCommisionsManifestTokenVendor",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Grandmother/GrandmotherTaskD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonC",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonA",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonA",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1415858946,
"Id": {
"$oid": "670a47b1872b2325705e746c"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Grandmother/GrandmotherTaskD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonB",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonA",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonA",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3178511462,
"Id": {
"$oid": "670a47b1872b2325705e746e"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Grandmother/GrandmotherTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonA",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonC",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3313207881,
"Id": {
"$oid": "670a47b1872b2325705e7471"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Grandmother/GrandmotherTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonB",
"ItemCount": 2,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2241288767,
"Id": {
"$oid": "670a47b1872b2325705e7472"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Grandmother/GrandmotherTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3395082536,
"Id": {
"$oid": "670a47b1872b2325705e7473"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Grandmother/GrandmotherTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonC",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 493457277,
"Id": {
"$oid": "670a47b1872b2325705e7474"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Grandmother/GrandmotherTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Deimos/EntratiFragmentCommonB",
"ItemCount": 2,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 4225814786,
"Id": {
"$oid": "670a47b1872b2325705e7475"
}
}
],
"PropertyTextHash": "58884EC7ECE7D22AD4BD9E9B436C37A8",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,254 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e02c96976e97d6b8049"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/HivemindCommisionsManifestWeaponsmith",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfGorgaricusSeedItem",
"ItemCount": 16,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2438288725,
"Id": {
"$oid": "66fd60b00ba592c4c95e7caf"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Game/FishBait/Infested/OrokinFishBaitA",
"ItemCount": 6,
"ProductCategory": "Consumables"
},
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfMapricoFruitItem",
"ItemCount": 21,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Game/FishBait/Infested/InfestedFishBaitA",
"ItemCount": 6,
"ProductCategory": "Consumables"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2431016296,
"Id": {
"$oid": "66fd60b00ba592c4c95e7cb2"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfMapricoFruitItem",
"ItemCount": 16,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 18484856,
"Id": {
"$oid": "66fd60b00ba592c4c95e7cb3"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfMapricoFruitItem",
"ItemCount": 14,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2278976516,
"Id": {
"$oid": "66fd60b00ba592c4c95e7cb4"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Game/FishBait/Infested/OrokinFishBaitA",
"ItemCount": 7,
"ProductCategory": "Consumables"
},
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/OrbStoneItem",
"ItemCount": 25,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Game/FishBait/Infested/InfestedFishBaitA",
"ItemCount": 6,
"ProductCategory": "Consumables"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3150323898,
"Id": {
"$oid": "66fd60b00ba592c4c95e7cb5"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfMapricoFruitItem",
"ItemCount": 8,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 3971758486,
"Id": {
"$oid": "66fd60b00ba592c4c95e7cb6"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfGorgaricusSeedItem",
"ItemCount": 17,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/OrbStoneItem",
"ItemCount": 18,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2512835718,
"Id": {
"$oid": "66fd60b00ba592c4c95e7cb7"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/OrbStoneItem",
"ItemCount": 8,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 825411410,
"Id": {
"$oid": "66fd60b00ba592c4c95e7cb8"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Father/FatherTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfGorgaricusSeedItem",
"ItemCount": 22,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/OrbStoneItem",
"ItemCount": 12,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2383349671,
"Id": {
"$oid": "66fd60b00ba592c4c95e7cb9"
}
}
],
"PropertyTextHash": "CE9413585756FA39B793A9814E74E49F",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,286 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5fb70313c96976e97d6be6fe"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/HivemindTokenVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Remedies/RemedySonB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishBPartItem",
"ItemCount": 36,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishEPartItem",
"ItemCount": 36,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 379215713,
"Id": {
"$oid": "66fd60b20ba592c4c95e9308"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Remedies/RemedyMotherB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosGenericInfestedFishPartItem",
"ItemCount": 80,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfGorgaricusSeedItem",
"ItemCount": 32,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 28,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2534781881,
"Id": {
"$oid": "66fd60b20ba592c4c95e9309"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Remedies/RemedyDaughterB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosUncommonGemAItem",
"ItemCount": 28,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 32,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishAPartItem",
"ItemCount": 32,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 1507786123,
"Id": {
"$oid": "66fd60b20ba592c4c95e930a"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Remedies/RemedySonA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosRareGemAItem",
"ItemCount": 15,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemAItem",
"ItemCount": 30,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfGorgaricusSeedItem",
"ItemCount": 21,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 616241643,
"Id": {
"$oid": "66fd60b20ba592c4c95e930b"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Remedies/RemedyOtakA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishBPartItem",
"ItemCount": 21,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishCPartItem",
"ItemCount": 27,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishEPartItem",
"ItemCount": 27,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2888479655,
"Id": {
"$oid": "66fd60b20ba592c4c95e930c"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Remedies/RemedyGrandmotherA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemBItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishBPartItem",
"ItemCount": 28,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfGorgaricusSeedItem",
"ItemCount": 24,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosCommonGemAItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2874726481,
"Id": {
"$oid": "66fd60b20ba592c4c95e930d"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Remedies/RemedyFatherA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosGenericSharedFishPartItem",
"ItemCount": 75,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Deimos/DeimosUncommonGemAItem",
"ItemCount": 27,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/OrbStoneItem",
"ItemCount": 30,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 274676857,
"Id": {
"$oid": "66fd60b20ba592c4c95e930e"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Deimos/Remedies/RemedyDaughterA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/InfestedMicroplanet/Resources/InfGorgaricusSeedItem",
"ItemCount": 24,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosInfestedFishBPartItem",
"ItemCount": 30,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Deimos/FishParts/DeimosGenericSharedFishPartItem",
"ItemCount": 51,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"LocTagRandSeed": 2487943761,
"Id": {
"$oid": "66fd60b20ba592c4c95e930f"
}
}
],
"PropertyTextHash": "C34BF0BEDEAF7CBB0EEBFFECDFD6646D",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,136 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e02c96976e97d6b8080"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/PetVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/AnimalTagInfestedNexiferaRare",
"PremiumPrice": [35, 35],
"Bin": "BIN_0",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e89f6"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/AnimalTagInfestedNexiferaUncommon",
"PremiumPrice": [22, 22],
"Bin": "BIN_0",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e89f7"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/AnimalTagInfestedMergooUncommon",
"PremiumPrice": [28, 28],
"Bin": "BIN_0",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e89f8"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/AnimalTagInfestedKdriveUncommon",
"PremiumPrice": [25, 25],
"Bin": "BIN_0",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e89f9"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/AnimalTagInfestedZongroCommon",
"PremiumPrice": [14, 14],
"Bin": "BIN_0",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e89fa"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/AnimalTagInfestedPredatorCommon",
"PremiumPrice": [12, 12],
"Bin": "BIN_0",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e89fb"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/AnimalTagInfestedMergooCommon",
"PremiumPrice": [13, 13],
"Bin": "BIN_0",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e89fc"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Deimos/AnimalTagInfestedKdriveCommon",
"PremiumPrice": [14, 14],
"Bin": "BIN_0",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b10ba592c4c95e89fd"
}
}
],
"PropertyTextHash": "F14C6B6A61D7585A10537995661F5220",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,61 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5f456e00c96976e97d6b7fd7"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Deimos/ProspectorVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Deimos/DeimosUncommonGemACutItem",
"PremiumPrice": [13, 13],
"Bin": "BIN_1",
"QuantityMultiplier": 10,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e93a8"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Deimos/DeimosCommonGemBCutItem",
"PremiumPrice": [7, 7],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e93a9"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Deimos/DeimosCommonGemACutItem",
"PremiumPrice": [7, 7],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e93aa"
}
}
],
"PropertyTextHash": "2BBC116116C757F6AF4FBC3B9BF754C8",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,321 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "64493ca759e9b164c86a2e14"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Duviri/AcrithisVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/DangerRoom/DangerRoomTileDuviriDragonArena",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_5",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b00ba592c4c95e7d88"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Components/FormaBlueprint",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b00ba592c4c95e7deb"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/UtilityUnlocker",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b00ba592c4c95e7dec"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 5000,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b00ba592c4c95e7ded"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/WeaponUtilityUnlocker",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b00ba592c4c95e7dee"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 5000,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "670c5e12576f461f1e5e739c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Plastids",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItemD",
"ItemCount": 40,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"Id": {
"$oid": "6710c312fa0b2c5cd85e73c3"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Duviri/DUVxPlanterHangingPot",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItemE",
"ItemCount": 51,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6710c312fa0b2c5cd85e73c6"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Duviri/DUVxPlanterPotB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriRockItem",
"ItemCount": 44,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6710c312fa0b2c5cd85e73c7"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/NeuralSensor",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItemA",
"ItemCount": 52,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 3,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"Id": {
"$oid": "6710c312fa0b2c5cd85e73c8"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/ControlModule",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItemF",
"ItemCount": 42,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 3,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"Id": {
"$oid": "6710c312fa0b2c5cd85e73c9"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/PhotoboothTileDuviriArenaOpera",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriProcessedItem",
"ItemCount": 240,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b00ba592c4c95e7ddd"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/CosmeticEnhancers/Utility/HealthWhileUsingChanneledAbilities",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriDragonDropItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b00ba592c4c95e7e01"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Boons/DuviriVendorBoonItem",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItemG",
"ItemCount": 50,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_4",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": true,
"Id": {
"$oid": "6711a412ba1ba01e405e739c"
}
}
],
"PropertyTextHash": "9EE40048EB685549ACA3D01AB1F65BF2",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,245 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "6579d82b553a20c6fc0067ca"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/EntratiLabs/EntratiLabVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalAmar",
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"Affiliation": "EntratiLabSyndicate",
"MinAffiliationRank": 5,
"ReductionPerPositiveRank": 0,
"IncreasePerNegativeRank": 0,
"StandingCost": 30000,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e920d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalBoreal",
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"Affiliation": "EntratiLabSyndicate",
"MinAffiliationRank": 5,
"ReductionPerPositiveRank": 0,
"IncreasePerNegativeRank": 0,
"StandingCost": 30000,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e920e"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Gameplay/NarmerSorties/ArchonCrystalNira",
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"Affiliation": "EntratiLabSyndicate",
"MinAffiliationRank": 5,
"ReductionPerPositiveRank": 0,
"IncreasePerNegativeRank": 0,
"StandingCost": 30000,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e920f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/EntratiLabs/ORKxLabStool",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabDogTagUncommon",
"ItemCount": 2,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabMiscItemA",
"ItemCount": 22,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/MurmurItem",
"ItemCount": 18,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9270"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/EntratiLabs/ORKxLabChairA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/MurmurItem",
"ItemCount": 15,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabMiscItemA",
"ItemCount": 19,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabMiscItemB",
"ItemCount": 19,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9271"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/EntratiLabs/ORKxLabLightWallCandleA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/MurmurItem",
"ItemCount": 12,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabDogTagCommon",
"ItemCount": 3,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9272"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/EntratiLabs/ORKxLabLightChandelierD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabMiscItemB",
"ItemCount": 8,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabDogTagCommon",
"ItemCount": 3,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9273"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/EntratiLabs/ORKxLabLightChandelierB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabMiscItemA",
"ItemCount": 12,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabDogTagCommon",
"ItemCount": 2,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9274"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/EntratiLabs/ORKxLabLightChandelierA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/MurmurItem",
"ItemCount": 15,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabMiscItemA",
"ItemCount": 13,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9275"
}
}
],
"PropertyTextHash": "44DA3839E6F7BDB32ACED53F2B0BE14E",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,97 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "6579d82b553a20c6fc0067ae"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/EntratiLabs/EntratiLabsCommisionsManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/EntratiLabs/LoidTaskC",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabMiscItemB",
"ItemCount": 17,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/MurmurItem",
"ItemCount": 30,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "670a2b928ac7854ac55e73d3"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/EntratiLabs/LoidTaskB",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/MurmurItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/EntratiLabMiscItemA",
"ItemCount": 228,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "670a2b928ac7854ac55e73d4"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/EntratiLabs/LoidTaskA",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/EntratiLab/Resources/MurmurItem",
"ItemCount": 15,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB",
"ItemCount": 1,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "670a2b928ac7854ac55e73d5"
}
}
],
"PropertyTextHash": "60C4D85A8DE5E6538AD23CDDFEEF0422",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,125 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5dbb4c41e966f7886c3ce939"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Hubs/IronwakeDondaVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/HarrowQuestKeyOrnament",
"ItemPrices": [
{
"ItemCount": 25,
"ItemType": "/Lotus/Types/Items/MiscItems/PrimeBucks",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e945f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/RivenModPack",
"ItemPrices": [
{
"ItemCount": 10,
"ItemType": "/Lotus/Types/Items/MiscItems/RivenFragment",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9468"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/CreditBundles/150000Credits",
"ItemPrices": [
{
"ItemCount": 5,
"ItemType": "/Lotus/Types/Items/MiscItems/RivenFragment",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9469"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
"ItemPrices": [
{
"ItemCount": 10,
"ItemType": "/Lotus/Types/Items/MiscItems/RivenFragment",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 35000,
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e946a"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/RivenModPack",
"ItemPrices": [
{
"ItemCount": 10,
"ItemType": "/Lotus/Types/Items/MiscItems/RivenFragment",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e946b"
}
}
],
"PropertyTextHash": "62B64A8065B7C0FA345895D4BC234621",
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
}
}
}

View File

@ -1,244 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5fb70313c96976e97d6be787"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Hubs/RailjackCrewMemberVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/SteelMeridianCrewMemberGeneratorStrong",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/RailjackMiscItems/IsosRailjackItem",
"ItemCount": 2220,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [2180000, 2180000],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "SteelMeridianSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 4185144421,
"Id": {
"$oid": "670daf92d21f34757a5e73da"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/NewLokaCrewMemberGeneratorStrong",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/RailjackMiscItems/IsosRailjackItem",
"ItemCount": 2130,
"ProductCategory": "MiscItems"
}
],
"RegularPrice": [1890000, 1890000],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "NewLokaSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 496053258,
"Id": {
"$oid": "670daf92d21f34757a5e73db"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/SteelMeridianCrewMemberGeneratorMediumVersionTwo",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/RailjackMiscItems/IsosRailjackItem",
"ItemCount": 440,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "SteelMeridianSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 2078883475,
"Id": {
"$oid": "670daf92d21f34757a5e73dc"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/NewLokaCrewMemberGeneratorMediumVersionTwo",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/RailjackMiscItems/AsteriteRailjackItem",
"ItemCount": 730,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "NewLokaSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 3890380934,
"Id": {
"$oid": "670daf92d21f34757a5e73dd"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/CephalonSudaCrewMemberGeneratorMediumVersionTwo",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/RailjackMiscItems/AsteriteRailjackItem",
"ItemCount": 720,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "CephalonSudaSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 3425148044,
"Id": {
"$oid": "670daf92d21f34757a5e73de"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/ArbitersCrewMemberGeneratorMediumVersionTwo",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/RailjackMiscItems/CubicsRailjackItem",
"ItemCount": 6500,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "ArbitersSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 2472754512,
"Id": {
"$oid": "670daf92d21f34757a5e73df"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/PerrinCrewMemberGeneratorVersionTwo",
"RegularPrice": [105000, 105000],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "PerrinSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 966238763,
"Id": {
"$oid": "670daf92d21f34757a5e73e0"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/NewLokaCrewMemberGeneratorVersionTwo",
"RegularPrice": [120000, 120000],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "NewLokaSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 356717213,
"Id": {
"$oid": "670daf92d21f34757a5e73e1"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Game/CrewShip/CrewMember/ArbitersCrewMemberGeneratorVersionTwo",
"RegularPrice": [120000, 120000],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"PurchaseQuantityLimit": 1,
"Affiliation": "ArbitersSyndicate",
"MinAffiliationRank": 0,
"ReductionPerPositiveRank": 0.1,
"IncreasePerNegativeRank": 0.5,
"AllowMultipurchase": false,
"LocTagRandSeed": 1969797050,
"Id": {
"$oid": "670daf92d21f34757a5e73e2"
}
}
],
"PropertyTextHash": "BE543CCC0A4F50A1D80CD2B523796EAE",
"RandomSeedType": "VRST_FLAVOUR_TEXT",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,301 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "598a090d9a4a313746fd1f24"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Ostron/MaskSalesmanManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Ostron/RevenantMask",
"ItemPrices": [
{
"ItemCount": 1,
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/CetusWispItem",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "63ed01ef4c37f93d0b797674"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyThumper",
"ItemPrices": [
{
"ItemCount": 2,
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/CetusWispItem",
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/BothUncommonFishBPartItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/NistlebrushItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Eidolon/CommonOreAAlloyAItem",
"ItemCount": 32,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "63ed01ef4c37f93d0b797675"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyThumperMedium",
"ItemPrices": [
{
"ItemCount": 4,
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/CetusWispItem",
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Eidolon/CommonGemBCutAItem",
"ItemCount": 24,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/BothUncommonFishAPartItem",
"ItemCount": 18,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/BothUncommonFishBPartItem",
"ItemCount": 27,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "63ed01ef4c37f93d0b797676"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Plushies/PlushyThumperLarge",
"ItemPrices": [
{
"ItemCount": 6,
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/CetusWispItem",
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Eidolon/CommonGemACutAItem",
"ItemCount": 35,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/BothCommonFishAPartItem",
"ItemCount": 95,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/NistlebrushItem",
"ItemCount": 60,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "63ed01ef4c37f93d0b797677"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/SynthicatorRecipes/FlareBlueBlueprint",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/BothUncommonFishBPartItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/DayCommonFishCPartItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6651291214e90115b91b50a1"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/SynthicatorRecipes/FlareRedBlueprint",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Eidolon/CommonOreAAlloyAItem",
"ItemCount": 37,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/BothUncommonFishAPartItem",
"ItemCount": 7,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6651291214e90115b91b50a2"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Ostron/VoltMask",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Eidolon/CommonOreBAlloyBItem",
"ItemCount": 34,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/GrokdrulItem",
"ItemCount": 17,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6651291214e90115b91b50a3"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Ostron/MagMask",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/DayCommonFishBPartItem",
"ItemCount": 16,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/ForestRodentPartItem",
"ItemCount": 5,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6651291214e90115b91b50a4"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Ostron/ExcaliburMask",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/BirdOfPreyPartItem",
"ItemCount": 5,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Gameplay/Eidolon/Resources/GrokdrulItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6651291214e90115b91b50a5"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Ostron/GrineerMask",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/DayCommonFishBPartItem",
"ItemCount": 20,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Eidolon/CommonOreAAlloyAItem",
"ItemCount": 31,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6651291214e90115b91b50a6"
}
}
],
"PropertyTextHash": "6AACA376DA34B35B5C16F1B40DBC017D",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,188 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "67dadc30e4b6e0e5979c8d6a"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/TheHex/Nova1999ConquestShopManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/1999StickersPackEchoesArchimedea",
"ItemPrices": [
{
"ItemCount": 10,
"ItemType": "/Lotus/Types/Items/MiscItems/1999ConquestBucks",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67db32b983b2ad79a9c1c18c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/1999StickersPackEchoesArchimedeaFree",
"ItemPrices": [
{
"ItemCount": 1,
"ItemType": "/Lotus/Types/Items/MiscItems/1999FreeStickersPack",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67db32b983b2ad79a9c1c18d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/1999StickersPackEchoesArchimedeaFixed",
"ItemPrices": [
{
"ItemCount": 1,
"ItemType": "/Lotus/Types/Items/MiscItems/1999FixedStickersPack",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67db32b983b2ad79a9c1c18e"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/SyndicateVosforPack",
"ItemPrices": [
{
"ItemCount": 6,
"ItemType": "/Lotus/Types/Items/MiscItems/1999ConquestBucks",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67db32b983b2ad79a9c1c18f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/StickerPictureFrame",
"ItemPrices": [
{
"ItemCount": 10,
"ItemType": "/Lotus/Types/Items/MiscItems/1999ConquestBucks",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67db32b983b2ad79a9c1c190"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/CosmeticEnhancers/Utility/AbilityRadiationProcsCreateUniversalOrbsOnKill",
"ItemPrices": [
{
"ItemCount": 5,
"ItemType": "/Lotus/Types/Items/MiscItems/1999ConquestBucks",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "67db32b983b2ad79a9c1c191"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/CosmeticEnhancers/Offensive/AbilityHeatProcsGiveCritChance",
"ItemPrices": [
{
"ItemCount": 5,
"ItemType": "/Lotus/Types/Items/MiscItems/1999ConquestBucks",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "67db32b983b2ad79a9c1c192"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/CosmeticEnhancers/Defensive/InvulnerabilityOnDeathOnMercyKill",
"ItemPrices": [
{
"ItemCount": 5,
"ItemType": "/Lotus/Types/Items/MiscItems/1999ConquestBucks",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": {
"$oid": "67db32b983b2ad79a9c1c193"
}
}
],
"PropertyTextHash": "CB7D0E807FD5E2BCD059195201D963B9",
"RequiredGoalTag": "",
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
}
}
}

View File

@ -1,94 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5991d5e6bcc718474ee90c15"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Ostron/PetVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/LisetPropOstBirdCage",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Gems/Eidolon/UncommonOreAAlloyAItem",
"ItemCount": 10,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/DayUncommonFishBPartItem",
"ItemCount": 8,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9a8e"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/KubrowColorPackDrahk",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/DayCommonFishBPartItem",
"ItemCount": 14,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/BothCommonFishBPartItem",
"ItemCount": 13,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9a8f"
}
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/KubrowColorPackFeral",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Fish/Eidolon/FishParts/BothCommonFishAPartItem",
"ItemCount": 19,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Gems/Eidolon/CommonOreBAlloyBItem",
"ItemCount": 34,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9a90"
}
}
],
"PropertyTextHash": "3D85F1A0A2B62734AE90370DEC214C26",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,62 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "59dfe591314805ffe1d47c0a"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Ostron/ProspectorVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Eidolon/RareGemACutAItem",
"PremiumPrice": [19, 19],
"Bin": "BIN_1",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e98f0"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Eidolon/CommonGemBCutAItem",
"PremiumPrice": [8, 8],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e98f1"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Eidolon/CommonGemACutAItem",
"PremiumPrice": [5, 5],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e98f2"
}
}
],
"MaxDailyPurchases": 0,
"PropertyTextHash": "773C6968D9A65506CD28DF28C768F0DA",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,126 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5be4a159b144f3cdf1c22edf"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Solaris/DebtTokenVendorRepossessionsManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Venus/SUToolBox",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Solaris/DebtTokenB",
"ItemCount": 6,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6711a412ba1ba01e405e739d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Venus/SUBookAOpen",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Solaris/DebtTokenC",
"ItemCount": 6,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6711a412ba1ba01e405e739e"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Venus/SUFoodCans",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Solaris/DebtTokenC",
"ItemCount": 7,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6711a412ba1ba01e405e739f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Venus/SUTechToolD",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Solaris/DebtTokenD",
"ItemCount": 5,
"ProductCategory": "MiscItems"
},
{
"ItemType": "/Lotus/Types/Items/Solaris/DebtTokenA",
"ItemCount": 15,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6711a412ba1ba01e405e73a0"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Venus/SUContainerCrate",
"ItemPrices": [
{
"ItemType": "/Lotus/Types/Items/Solaris/DebtTokenA",
"ItemCount": 9,
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "6711a412ba1ba01e405e73a1"
}
}
],
"PropertyTextHash": "E0E83157D73468DC578403CB9EBA9DA6",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,61 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "5be4a159b144f3cdf1c22ebb"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Solaris/ProspectorVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Solaris/SolarisRareGemACutItem",
"PremiumPrice": [20, 20],
"Bin": "BIN_1",
"QuantityMultiplier": 5,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9777"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Solaris/SolarisCommonGemBCutItem",
"PremiumPrice": [10, 10],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9778"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/Gems/Solaris/SolarisCommonGemACutItem",
"PremiumPrice": [8, 8],
"Bin": "BIN_0",
"QuantityMultiplier": 20,
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9779"
}
}
],
"PropertyTextHash": "A5756A21991FF49CFA7D096B4026515B",
"Expiry": {
"$date": {
"$numberLong": "9999999000000"
}
}
}
}

View File

@ -1,459 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "67dadc30e4b6e0e5979c8d56"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/TheHex/Temple1999VendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TempleBlueprint",
"ItemPrices": [
{
"ItemCount": 195,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c18c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TempleSystemsBlueprint",
"ItemPrices": [
{
"ItemCount": 65,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c18d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TempleChassisBlueprint",
"ItemPrices": [
{
"ItemCount": 65,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c18e"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/WarframeRecipes/TempleHelmetBlueprint",
"ItemPrices": [
{
"ItemCount": 65,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c18f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Weapons/1999EntHybridPistolBlueprint",
"ItemPrices": [
{
"ItemCount": 120,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c190"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/1999EntHybridPistolBarrelBlueprint",
"ItemPrices": [
{
"ItemCount": 60,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c191"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/1999EntHybridPistolReceiverBlueprint",
"ItemPrices": [
{
"ItemCount": 60,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c192"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Weapons/WeaponParts/1999EntHybridPistolStockBlueprint",
"ItemPrices": [
{
"ItemCount": 60,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c193"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumDrumCoreKitA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c194"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumDrumCymbalA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c195"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumDrumFloorTomA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c196"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumDrumSnareA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c197"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c198"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseB",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c199"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseC",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19a"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseD",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19b"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseE",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumEquipmentCaseF",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/Hollvania/LASxStadiumSynthKeyboardA",
"ItemPrices": [
{
"ItemCount": 30,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c19e"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/PhotoBooth/Vania/PhotoboothTileVaniaObjTempleDefense",
"ItemPrices": [
{
"ItemCount": 100,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": false,
"Id": {
"$oid": "67dadc30641da66dc5c1c19f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
"ItemPrices": [
{
"ItemCount": 110,
"ItemType": "/Lotus/Types/Gameplay/1999Wf/Resources/1999ResourceDefense",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 6000,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 7,
"AllowMultipurchase": true,
"Id": {
"$oid": "67dadc30641da66dc5c1c1a5"
}
}
],
"PropertyTextHash": "20B13D9EB78FEC80EA32D0687F5BA1AE",
"RequiredGoalTag": "",
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
}
}
}

View File

@ -1,603 +0,0 @@
{
"VendorInfo": {
"_id": {
"$oid": "63ed01efbdaa38891767bac9"
},
"TypeName": "/Lotus/Types/Game/VendorManifests/Hubs/TeshinHardModeVendorManifest",
"ItemManifest": [
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinArmsBlueprint",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9947"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinBodyBlueprint",
"ItemPrices": [
{
"ItemCount": 25,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9948"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinHeadBlueprint",
"ItemPrices": [
{
"ItemCount": 20,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9949"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/OperatorArmour/HardMode/OperatorTeshinLegsBlueprint",
"ItemPrices": [
{
"ItemCount": 25,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e994a"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/WeaponPrimaryArcaneUnlocker",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e994b"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/WeaponSecondaryArcaneUnlocker",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e994c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Components/FormaStanceBlueprint",
"ItemPrices": [
{
"ItemCount": 10,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e994d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Effects/OrbsEphemera",
"ItemPrices": [
{
"ItemCount": 3,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e994e"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Skins/Effects/TatsuSkullEphemera",
"ItemPrices": [
{
"ItemCount": 85,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e994f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawShotgunRandomMod",
"ItemPrices": [
{
"ItemCount": 75,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9950"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Recipes/Components/UmbraFormaBlueprint",
"ItemPrices": [
{
"ItemCount": 150,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9951"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
"ItemPrices": [
{
"ItemCount": 55,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 50000,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9952"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawModularPistolRandomMod",
"ItemPrices": [
{
"ItemCount": 75,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9953"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Forma",
"ItemPrices": [
{
"ItemCount": 75,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 3,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9954"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawModularMeleeRandomMod",
"ItemPrices": [
{
"ItemCount": 75,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9955"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/FusionBundles/EvergreenLoginRewardFusionBundle",
"ItemPrices": [
{
"ItemCount": 150,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9956"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Randomized/RawRifleRandomMod",
"ItemPrices": [
{
"ItemCount": 75,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e9957"
}
},
{
"StoreItem": "/Lotus/StoreItems/Upgrades/Mods/Shotgun/WeaponRecoilReductionMod",
"ItemPrices": [
{
"ItemCount": 35,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9958"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/TeshinBobbleHead",
"ItemPrices": [
{
"ItemCount": 35,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e9959"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/ImageGaussVED",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e995a"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/ImageGrendelVED",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e995b"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageProteaAction",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e995c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/ShipDecos/TeaSet",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e995d"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/StoreItems/AvatarImages/AvatarImageXakuAction",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e995e"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/RivenIdentifier",
"ItemPrices": [
{
"ItemCount": 20,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "2051240400000"
}
},
"PurchaseQuantityLimit": 1,
"RotatedWeekly": true,
"AllowMultipurchase": false,
"Id": {
"$oid": "66fd60b20ba592c4c95e995f"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/BoosterPacks/RandomSyndicateProjectionPack",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
},
"PurchaseQuantityLimit": 25,
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e997c"
}
},
{
"StoreItem": "/Lotus/StoreItems/Types/Items/MiscItems/Kuva",
"ItemPrices": [
{
"ItemCount": 15,
"ItemType": "/Lotus/Types/Items/MiscItems/SteelEssence",
"ProductCategory": "MiscItems"
}
],
"Bin": "BIN_0",
"QuantityMultiplier": 10000,
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
},
"PurchaseQuantityLimit": 25,
"AllowMultipurchase": true,
"Id": {
"$oid": "66fd60b20ba592c4c95e997d"
}
}
],
"PropertyTextHash": "0A0F20AFA748FBEE490510DBF5A33A0D",
"Expiry": {
"$date": {
"$numberLong": "604800000"
}
}
}
}

View File

@ -1,75 +0,0 @@
{
"VendorInfo": {
"_id": { "$oid": "62a20ba667e5d3797540d831" },
"TypeName": "/Lotus/Types/Game/VendorManifests/Zariman/ZarimanCommisionsManifestArchimedean",
"ItemManifest": [
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskE",
"ItemPrices": [
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB", "ItemCount": 4, "ProductCategory": "MiscItems" },
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemA", "ItemCount": 6, "ProductCategory": "MiscItems" }
],
"Bin": "BIN_4",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "9999999000000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": { "$oid": "6678b612aa3d8ee5c2597299" }
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskD",
"ItemPrices": [
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemA", "ItemCount": 5, "ProductCategory": "MiscItems" },
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB", "ItemCount": 3, "ProductCategory": "MiscItems" }
],
"Bin": "BIN_3",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "9999999000000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": { "$oid": "6678b612aa3d8ee5c259729a" }
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskC",
"ItemPrices": [
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidWraithItem", "ItemCount": 15, "ProductCategory": "MiscItems" },
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanDogTagUncommon", "ItemCount": 1, "ProductCategory": "MiscItems" }
],
"Bin": "BIN_2",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "9999999000000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": { "$oid": "6678b612aa3d8ee5c259729b" }
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskB",
"ItemPrices": [
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/VoidWraithItem", "ItemCount": 4, "ProductCategory": "MiscItems" },
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB", "ItemCount": 1, "ProductCategory": "MiscItems" }
],
"Bin": "BIN_1",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "9999999000000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": { "$oid": "6678b612aa3d8ee5c259729c" }
},
{
"StoreItem": "/Lotus/Types/StoreItems/Packages/Tasks/Zariman/AchimedeanTaskA",
"ItemPrices": [
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemB", "ItemCount": 1, "ProductCategory": "MiscItems" },
{ "ItemType": "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanMiscItemA", "ItemCount": 2, "ProductCategory": "MiscItems" }
],
"Bin": "BIN_0",
"QuantityMultiplier": 1,
"Expiry": { "$date": { "$numberLong": "9999999000000" } },
"PurchaseQuantityLimit": 1,
"AllowMultipurchase": false,
"Id": { "$oid": "6678b612aa3d8ee5c259729d" }
}
],
"PropertyTextHash": "F43F0ED811985EEF856970A8342EF322",
"Expiry": { "$date": { "$numberLong": "9999999000000" } }
}
}

View File

@ -1,83 +0,0 @@
{
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeEquilibrium": "+20% Energy from Health pickups, +20% Health from Energy pickups",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeEquilibriumMythic": "+30% Energy from Health pickups, +30% Health from Energy pickups",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeMeleeCritDamage": "+25% Melee Critical Damage",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeMeleeCritDamageMythic": "+37.5% Melee Critical Damage",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradePrimaryStatusChance": "+25% Primary Status Chance",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradePrimaryStatusChanceMythic": "+37.5% Primary Status Chance",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeSecondaryCritChance": "+25% Secondary Critical Chance",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeSecondaryCritChanceMythic": "+37.5% Secondary Critical Chance",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityDuration": "+10% Ability Duration",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityDurationMythic": "+15% Ability Duration",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityStrength": "+10% Ability Strength",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeAbilityStrengthMythic": "+15% Ability Strength",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMax": "+150 Armor",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeArmourMaxMythic": "+225 Armor",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeBlastProc": "+5 Shields on inflicting Blast Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeBlastProcMythic": "+7.5 Shields on inflicting Blast Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCastingSpeed": "+25% Casting Speed",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCastingSpeedMythic": "+37.5% Casting Speed",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCorrosiveDamageBoost": "+10% Ability Damage on enemies affected by Corrosion Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCorrosiveDamageBoostMythic": "+15% Ability Damage on enemies affected by Corrosion Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCorrosiveStack": "Increase max stacks of Corrosion Status by +2",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCorrosiveStackMythic": "Increase max stacks of Corrosion Status by +3",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCritDamageBoost": "+25% Melee Critical Damage (Doubles over 500 Energy)",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeCritDamageBoostMythic": "+37% Melee Critical Damage (Doubles over 500 Energy)",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeElectricDamage": "+30% Primary Electricity Damage (+10% per additional Shard)",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeElectricDamageMythic": "+45% Primary Electricity Damage (+15% per additional Shard)",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeElectricDamageBoost": "+10% Ability Damage on enemies affected by Electricity Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeElectricDamageBoostMythic": "+15% Ability Damage on enemies affected by Electricity Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeEnergyMax": "+50 Energy Max",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeEnergyMaxMythic": "+75 Energy Max",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeGlobeEffectEnergy": "+50% Energy Orb Effectiveness",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeGlobeEffectEnergyMythic": "+75% Energy Orb Effectiveness",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeGlobeEffectHealth": "+100% Health Orb Effectiveness",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeGlobeEffectHealthMythic": "+150% Health Orb Effectiveness",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeHealthMax": "+150 Health",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeHealthMaxMythic": "+225 Health",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeHPBoostFromImpact": "+1 Health per enemy killed with Blast Damage (Max 300 Health)",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeHPBoostFromImpactMythic": "+2 Health per enemy killed with Blast Damage (Max 450 Health)",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeParkourVelocity": "+15% Parkour Velocity",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeParkourVelocityMythic": "+22.5% Parkour Velocity",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRadiationDamageBoost": "+10% Ability Damage on enemies affected by Radiation Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRadiationDamageBoostMythic": "+15% Ability Damage on enemies affected by Radiation Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegen": "+5 Health Regen/s",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeRegenMythic": "+7.5 Health Regen/s",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeShieldMax": "+150 Shield",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeShieldMaxMythic": "+225 Shield",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeStartingEnergy": "+30% Energy on Spawn",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeStartingEnergyMythic": "+45% Energy on Spawn",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeToxinDamage": "+30% Toxin Status Effect Damage",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeToxinDamageMythic": "+45% Toxin Status Effect Damage",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeToxinHeal": "+2 Health on damaging enemies with Toxin Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWarframeToxinHealMythic": "+3 Health on damaging enemies with Toxin Status",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWeaponCritBoostFromHeat": "+1% Secondary Critical Chance per Heat-affected enemy killed (Max 50%)",
"/Lotus/Upgrades/Invigorations/ArchonCrystalUpgrades/ArchonCrystalUpgradeWeaponCritBoostFromHeatMythic": "+1.5% Secondary Critical Chance per Heat-affected enemy killed (Max 75%)",
"/Lotus/Upgrades/Mods/Warframe/AvatarAbilityRangeMod": "+7.5% Ability Range",
"/Lotus/Upgrades/Mods/Warframe/AvatarAbilityEfficiencyMod": "+5% Ability Efficiency",
"/Lotus/Upgrades/Mods/Warframe/AvatarEnergyRegenMod": "+0.5 Energy Regen/s",
"/Lotus/Upgrades/Mods/Warframe/AvatarEnemyRadarMod": "+5m Enemy Radar",
"/Lotus/Upgrades/Mods/Warframe/AvatarLootRadarMod": "+7m Loot Radar",
"/Lotus/Upgrades/Mods/Rifle/WeaponAmmoMaxMod": "+15% Ammo Max",
"/Lotus/Upgrades/Mods/Aura/EnemyArmorReductionAuraMod": "-3% Enemy Armor",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/OnExecutionAmmoMod": "100% Primary and Secondary Magazine Refill on Mercy",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/OnExecutionHealthDropMod": "100% chance to drop a Health Orb on Mercy",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/OnExecutionEnergyDropMod": "50% chance to drop an Energy Orb on Mercy",
"/Lotus/Upgrades/Mods/DataSpike/Cipher/OnFailHackResetMod": "+50% to retry on Hacking failure",
"/Lotus/Upgrades/Mods/DataSpike/Cipher/DamageReductionOnHackMod": "75% Damage Reduction while Hacking",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/OnExecutionReviveCompanionMod": "Mercy Kills reduce Companion Recovery by 15s",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/OnExecutionParkourSpeedMod": "+60% Parkour Speed after a Mercy for 15s",
"/Lotus/Upgrades/Mods/Warframe/AvatarTimeLimitIncreaseMod": "+8s to Hacking",
"/Lotus/Upgrades/Mods/DataSpike/Cipher/ElectrifyOnHackMod": "Shock enemies within 20m while Hacking",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/OnExecutionTerrifyMod": "50% chance for enemies within 15m to cower in fear for 8 seconds on Mercy",
"/Lotus/Upgrades/Mods/DataSpike/Cipher/OnHackLockersMod": "Unlock 5 lockers within 20m after Hacking",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/OnExecutionBlindMod": "Blind enemies within 18m on Mercy",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/OnExecutionDrainPowerMod": "100% chance for next ability cast to gain +50% Ability Strength on Mercy",
"/Lotus/Upgrades/Mods/DataSpike/Cipher/OnHackSprintSpeedMod": "+75% Sprint Speed for 15s after Hacking",
"/Lotus/Upgrades/Mods/DataSpike/Assassin/SwiftExecuteMod": "Speed of Mercy Kills increased by 50%",
"/Lotus/Upgrades/Mods/DataSpike/Cipher/OnHackInvisMod": "Invisible for 15 seconds after hacking"
}

View File

@ -0,0 +1,154 @@
{
"VoidT1": [
"SolNode23",
"SolNode66",
"SolNode45",
"SolNode41",
"SolNode59",
"SolNode39",
"SolNode75",
"SolNode113",
"SolNode85",
"SolNode58",
"SolNode101",
"SolNode109",
"SolNode26",
"SolNode15",
"SolNode61",
"SolNode123",
"SolNode16",
"SolNode79",
"SolNode2",
"SolNode22",
"SolNode68",
"SolNode89",
"SolNode11",
"SolNode46",
"SolNode36",
"SolNode27",
"SolNode14",
"SolNode106",
"SolNode30",
"SolNode107",
"SolNode63",
"SolNode128"
],
"VoidT2": [
"SolNode141",
"SolNode149",
"SolNode10",
"SolNode93",
"SettlementNode11",
"SolNode137",
"SolNode132",
"SolNode73",
"SolNode82",
"SolNode25",
"SolNode88",
"SolNode126",
"SolNode135",
"SolNode74",
"SettlementNode15",
"SolNode147",
"SolNode67",
"SolNode20",
"SolNode42",
"SolNode18",
"SolNode31",
"SolNode139",
"SettlementNode12",
"SolNode100",
"SolNode140",
"SolNode70",
"SettlementNode1",
"SettlementNode14",
"SolNode50",
"SettlementNode2",
"SolNode146",
"SettlementNode3",
"SolNode97",
"SolNode125",
"SolNode19",
"SolNode121",
"SolNode96",
"SolNode131"
],
"VoidT3": [
"SolNode62",
"SolNode17",
"SolNode403",
"SolNode6",
"SolNode118",
"SolNode211",
"SolNode217",
"SolNode401",
"SolNode64",
"SolNode405",
"SolNode84",
"SolNode402",
"SolNode408",
"SolNode122",
"SolNode57",
"SolNode216",
"SolNode205",
"SolNode215",
"SolNode404",
"SolNode209",
"SolNode406",
"SolNode204",
"SolNode203",
"SolNode409",
"SolNode400",
"SolNode212",
"SolNode1",
"SolNode412",
"SolNode49",
"SolNode78",
"SolNode410",
"SolNode407",
"SolNode220"
],
"VoidT4": [
"SolNode188",
"SolNode403",
"SolNode189",
"SolNode21",
"SolNode102",
"SolNode171",
"SolNode196",
"SolNode184",
"SolNode185",
"SolNode76",
"SolNode195",
"SolNode164",
"SolNode401",
"SolNode405",
"SolNode56",
"SolNode402",
"SolNode408",
"SolNode4",
"SolNode181",
"SolNode406",
"SolNode162",
"SolNode72",
"SolNode407",
"SolNode177",
"SolNode404",
"SolNode400",
"SolNode409",
"SolNode43",
"SolNode166",
"SolNode172",
"SolNode412",
"SolNode187",
"SolNode38",
"SolNode175",
"SolNode81",
"SolNode48",
"SolNode410",
"SolNode153",
"SolNode173"
],
"VoidT5": ["SolNode747", "SolNode743", "SolNode742", "SolNode744", "SolNode745", "SolNode748", "SolNode746", "SolNode741"],
"VoidT6": ["SolNode717", "SolNode309", "SolNode718", "SolNode232", "SolNode230", "SolNode310"]
}

View File

@ -327,195 +327,6 @@
"Nodes": []
}
],
"ActiveMissions": [
{
"_id": { "$oid": "663a7509d93367863785932d" },
"Region": 15,
"Seed": 80795,
"Activation": { "$date": { "$numberLong": "1715107081517" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode400",
"MissionType": "MT_EXTERMINATION",
"Modifier": "VoidT3",
"Hard": true
},
{
"_id": { "$oid": "663a75f959a5964cadb39879" },
"Region": 19,
"Seed": 32067,
"Activation": { "$date": { "$numberLong": "1715107321237" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode747",
"MissionType": "MT_INTEL",
"Modifier": "VoidT5",
"Hard": true
},
{
"_id": { "$oid": "663a779d3e347839ff301814" },
"Region": 7,
"Seed": 51739,
"Activation": { "$date": { "$numberLong": "1715107741454" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode64",
"MissionType": "MT_TERRITORY",
"Modifier": "VoidT3"
},
{
"_id": { "$oid": "663a77d916c199f4644ee67d" },
"Region": 17,
"Seed": 61179,
"Activation": { "$date": { "$numberLong": "1715107801647" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode718",
"MissionType": "MT_ALCHEMY",
"Modifier": "VoidT6"
},
{
"_id": { "$oid": "663a78c98a609b49b8410726" },
"Region": 3,
"Seed": 9520,
"Activation": { "$date": { "$numberLong": "1715108041501" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode79",
"MissionType": "MT_INTEL",
"Modifier": "VoidT1",
"Hard": true
},
{
"_id": { "$oid": "663a7df15eeabaac79b0a061" },
"Region": 6,
"Seed": 48861,
"Activation": { "$date": { "$numberLong": "1715109361974" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode67",
"MissionType": "MT_INTEL",
"Modifier": "VoidT2",
"Hard": true
},
{
"_id": { "$oid": "663a7df25eeabaac79b0a062" },
"Region": 5,
"Seed": 13550,
"Activation": { "$date": { "$numberLong": "1715109361974" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode10",
"MissionType": "MT_SABOTAGE",
"Modifier": "VoidT2",
"Hard": true
},
{
"_id": { "$oid": "663a83cdec0d5181435f1324" },
"Region": 19,
"Seed": 39392,
"Activation": { "$date": { "$numberLong": "1715110861506" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode742",
"MissionType": "MT_DEFENSE",
"Modifier": "VoidT5"
},
{
"_id": { "$oid": "663a83cdec0d5181435f1325" },
"Region": 19,
"Seed": 88668,
"Activation": { "$date": { "$numberLong": "1715110861506" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode743",
"MissionType": "MT_MOBILE_DEFENSE",
"Modifier": "VoidT5"
},
{
"_id": { "$oid": "663a83cdec0d5181435f1326" },
"Region": 19,
"Seed": 73823,
"Activation": { "$date": { "$numberLong": "1715110861506" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode741",
"MissionType": "MT_ASSAULT",
"Modifier": "VoidT5"
},
{
"_id": { "$oid": "663a878d23d1514873170466" },
"Region": 9,
"Seed": 88696,
"Activation": { "$date": { "$numberLong": "1715111821951" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode4",
"MissionType": "MT_EXTERMINATION",
"Modifier": "VoidT4",
"Hard": true
},
{
"_id": { "$oid": "663a887d4903098c10992fe6" },
"Region": 6,
"Seed": 66337,
"Activation": { "$date": { "$numberLong": "1715112061729" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode18",
"MissionType": "MT_TERRITORY",
"Modifier": "VoidT2"
},
{
"_id": { "$oid": "663a887d4903098c10992fe7" },
"Region": 10,
"Seed": 5135,
"Activation": { "$date": { "$numberLong": "1715112061729" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode149",
"MissionType": "MT_DEFENSE",
"Modifier": "VoidT2"
},
{
"_id": { "$oid": "663a8931586c301b1fbe63d3" },
"Region": 15,
"Seed": 32180,
"Activation": { "$date": { "$numberLong": "1715112241196" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode408",
"MissionType": "MT_DEFENSE",
"Modifier": "VoidT4"
},
{
"_id": { "$oid": "663a8931586c301b1fbe63d4" },
"Region": 12,
"Seed": 22521,
"Activation": { "$date": { "$numberLong": "1715112241196" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode181",
"MissionType": "MT_EXTERMINATION",
"Modifier": "VoidT4"
},
{
"_id": { "$oid": "663a8931586c301b1fbe63d5" },
"Region": 2,
"Seed": 28500,
"Activation": { "$date": { "$numberLong": "1715112241196" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode128",
"MissionType": "MT_EXTERMINATION",
"Modifier": "VoidT1"
},
{
"_id": { "$oid": "663a8931586c301b1fbe63d6" },
"Region": 3,
"Seed": 24747,
"Activation": { "$date": { "$numberLong": "1715112241196" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode26",
"MissionType": "MT_DEFENSE",
"Modifier": "VoidT1"
},
{
"_id": { "$oid": "663a8931586c301b1fbe63d7" },
"Region": 17,
"Seed": 63914,
"Activation": { "$date": { "$numberLong": "1715112241196" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"Node": "SolNode717",
"MissionType": "MT_SURVIVAL",
"Modifier": "VoidT6",
"Hard": true
}
],
"NodeOverrides": [
{ "_id": { "$oid": "549b18e9b029cef5991d6aec" }, "Node": "EuropaHUB", "Hide": true },
{ "_id": { "$oid": "54a1737aeb658f6cbccf70ff" }, "Node": "ErisHUB", "Hide": true },
@ -2562,50 +2373,6 @@
]
}
],
"VoidStorms": [
{
"_id": { "$oid": "663a7581ced28e18f694b550" },
"Node": "CrewBattleNode519",
"Activation": { "$date": { "$numberLong": "1715109601821" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"ActiveMissionTier": "VoidT1"
},
{
"_id": { "$oid": "663a7581ced28e18f694b551" },
"Node": "CrewBattleNode515",
"Activation": { "$date": { "$numberLong": "1715109601825" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"ActiveMissionTier": "VoidT1"
},
{
"_id": { "$oid": "663a7581ced28e18f694b554" },
"Node": "CrewBattleNode536",
"Activation": { "$date": { "$numberLong": "1715109601832" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"ActiveMissionTier": "VoidT4"
},
{
"_id": { "$oid": "663a7581ced28e18f694b555" },
"Node": "CrewBattleNode539",
"Activation": { "$date": { "$numberLong": "1715109601834" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"ActiveMissionTier": "VoidT4"
},
{
"_id": { "$oid": "663a7581ced28e18f694b553" },
"Node": "CrewBattleNode521",
"Activation": { "$date": { "$numberLong": "1715109601829" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"ActiveMissionTier": "VoidT3"
},
{
"_id": { "$oid": "663a7581ced28e18f694b552" },
"Node": "CrewBattleNode535",
"Activation": { "$date": { "$numberLong": "1715109601827" } },
"Expiry": { "$date": { "$numberLong": "2000000000000" } },
"ActiveMissionTier": "VoidT2"
}
],
"PrimeAccessAvailability": { "State": "PRIME1" },
"PrimeVaultAvailabilities": [false, false, false, false, false],
"PrimeTokenAvailability": true,

View File

@ -37,7 +37,7 @@
<li class="nav-item dropdown user-dropdown">
<button class="nav-link dropdown-toggle displayname" data-bs-toggle="dropdown" aria-expanded="false"></button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/webui/" onclick="logout();" data-loc="navbar_logout"></a></li>
<li><a class="dropdown-item" href="/webui/" onclick="doLogout();" data-loc="navbar_logout"></a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="event.preventDefault();renameAccount();" data-loc="navbar_renameAccount"></a></li>
<li><a class="dropdown-item" href="#" onclick="event.preventDefault();deleteAccount();" data-loc="navbar_deleteAccount"></a></li>
@ -416,6 +416,20 @@
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card mb-3" style="height: 400px;">
<h5 class="card-header" data-loc="inventory_Boosters"></h5>
<div class="card-body overflow-auto">
<form class="input-group mb-3" onsubmit="doAcquireBoosters();return false;">
<input class="form-control" id="acquire-type-Boosters" list="datalist-Boosters" />
<button class="btn btn-primary" type="submit" data-loc="general_addButton"></button>
</form>
<table class="table table-hover w-100">
<tbody id="Boosters-list"></tbody>
</table>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<h5 class="card-header" data-loc="general_bulkActions"></h5>
@ -557,7 +571,7 @@
<div id="server-settings-no-perms" class="d-none">
<p class="card-text" data-loc="cheats_administratorRequirement"></p>
</div>
<form id="server-settings" class="d-none" onsubmit="doChangeSettings();return false;">
<div id="server-settings" class="d-none">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="skipTutorial" />
<label class="form-check-label" for="skipTutorial" data-loc="cheats_skipTutorial"></label>
@ -570,10 +584,6 @@
<input class="form-check-input" type="checkbox" id="unlockAllScans" />
<label class="form-check-label" for="unlockAllScans" data-loc="cheats_unlockAllScans"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllMissions" />
<label class="form-check-label" for="unlockAllMissions" data-loc="cheats_unlockAllMissions"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="infiniteCredits" />
<label class="form-check-label" for="infiniteCredits" data-loc="cheats_infiniteCredits"></label>
@ -590,13 +600,29 @@
<input class="form-check-input" type="checkbox" id="infiniteRegalAya" />
<label class="form-check-label" for="infiniteRegalAya" data-loc="cheats_infiniteRegalAya"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="infiniteHelminthMaterials" />
<label class="form-check-label" for="infiniteHelminthMaterials" data-loc="cheats_infiniteHelminthMaterials"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="claimingBlueprintRefundsIngredients" />
<label class="form-check-label" for="claimingBlueprintRefundsIngredients" data-loc="cheats_claimingBlueprintRefundsIngredients"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="infiniteHelminthMaterials" />
<label class="form-check-label" for="infiniteHelminthMaterials" data-loc="cheats_infiniteHelminthMaterials"></label>
<input class="form-check-input" type="checkbox" id="dontSubtractPurchaseCreditCost" />
<label class="form-check-label" for="dontSubtractPurchaseCreditCost" data-loc="cheats_dontSubtractPurchaseCreditCost"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="dontSubtractPurchasePlatinumCost" />
<label class="form-check-label" for="dontSubtractPurchasePlatinumCost" data-loc="cheats_dontSubtractPurchasePlatinumCost"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="dontSubtractPurchaseItemCost" />
<label class="form-check-label" for="dontSubtractPurchaseItemCost" data-loc="cheats_dontSubtractPurchaseItemCost"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="dontSubtractPurchaseStandingCost" />
<label class="form-check-label" for="dontSubtractPurchaseStandingCost" data-loc="cheats_dontSubtractPurchaseStandingCost"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="dontSubtractVoidTraces" />
@ -674,10 +700,18 @@
<input class="form-check-input" type="checkbox" id="noKimCooldowns" />
<label class="form-check-label" for="noKimCooldowns" data-loc="cheats_noKimCooldowns"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="fullyStockedVendors" />
<label class="form-check-label" for="fullyStockedVendors" data-loc="cheats_fullyStockedVendors"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="syndicateMissionsRepeatable" />
<label class="form-check-label" for="syndicateMissionsRepeatable" data-loc="cheats_syndicateMissionsRepeatable"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllProfitTakerStages" />
<label class="form-check-label" for="unlockAllProfitTakerStages" data-loc="cheats_unlockAllProfitTakerStages"></label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="instantFinishRivenChallenge" />
<label class="form-check-label" for="instantFinishRivenChallenge" data-loc="cheats_instantFinishRivenChallenge"></label>
@ -718,12 +752,29 @@
<input class="form-check-input" type="checkbox" id="fastClanAscension" />
<label class="form-check-label" for="fastClanAscension" data-loc="cheats_fastClanAscension"></label>
</div>
<div class="form-group mt-2">
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" />
<div class="form-check">
<input class="form-check-input" type="checkbox" id="missionsCanGiveAllRelics" />
<label class="form-check-label" for="missionsCanGiveAllRelics" data-loc="cheats_missionsCanGiveAllRelics"></label>
</div>
<button class="btn btn-primary mt-3" type="submit" data-loc="cheats_saveSettings"></button>
</form>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="unlockAllSimarisResearchEntries" />
<label class="form-check-label" for="unlockAllSimarisResearchEntries" data-loc="cheats_unlockAllSimarisResearchEntries"></label>
</div>
<form class="form-group mt-2" onsubmit="doSaveConfig('spoofMasteryRank'); return false;">
<label class="form-label" for="spoofMasteryRank" data-loc="cheats_spoofMasteryRank"></label>
<div class="input-group">
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" />
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
</div>
</form>
<form class="form-group mt-2" onsubmit="doSaveConfig('nightwaveStandingMultiplier'); return false;">
<label class="form-label" for="nightwaveStandingMultiplier" data-loc="cheats_nightwaveStandingMultiplier"></label>
<div class="input-group">
<input class="form-control" id="nightwaveStandingMultiplier" type="number" min="1" max="1000000" value="1" />
<button class="btn btn-primary" type="submit" data-loc="cheats_save"></button>
</div>
</form>
</div>
</div>
</div>
</div>
@ -732,9 +783,11 @@
<h5 class="card-header" data-loc="cheats_account"></h5>
<div class="card-body">
<div class="mb-2 d-flex flex-wrap gap-2">
<button class="btn btn-primary" onclick="debounce(doUnlockAllMissions);" data-loc="cheats_unlockAllMissions"></button>
<button class="btn btn-primary" onclick="doUnlockAllFocusSchools();" data-loc="cheats_unlockAllFocusSchools"></button>
<button class="btn btn-primary" onclick="doHelminthUnlockAll();" data-loc="cheats_helminthUnlockAll"></button>
<button class="btn btn-primary" onclick="doIntrinsicsUnlockAll();" data-loc="cheats_intrinsicsUnlockAll"></button>
<button class="btn btn-primary" onclick="debounce(doMaxPlexus);" data-loc="inventory_maxPlexus"></button>
</div>
<form class="mt-2" onsubmit="doChangeSupportedSyndicate(); return false;">
<label class="form-label" for="changeSyndicate" data-loc="cheats_changeSupportedSyndicate"></label>
@ -750,8 +803,12 @@
</div>
<div data-route="/webui/import" data-title="Import | OpenWF WebUI">
<p data-loc="import_importNote"></p>
<textarea class="form-control" id="import-inventory"></textarea>
<textarea class="form-control" id="import-inventory" style="height: calc(100vh - 300px)"></textarea>
<button class="btn btn-primary mt-3" onclick="doImport();" data-loc="import_submit"></button>
<p class="mt-3 mb-1" data-loc="import_samples"></p>
<ul>
<li><a href="#" onclick="event.preventDefault();setImportSample('maxFocus');" data-loc="import_samples_maxFocus"></a></li>
</ul>
</div>
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3"></div>
@ -804,6 +861,7 @@
<datalist id="datalist-ModularParts-CATBROW_MUTAGEN"></datalist>
<datalist id="datalist-ModularParts-KUBROW_ANTIGEN"></datalist>
<datalist id="datalist-ModularParts-KUBROW_MUTAGEN"></datalist>
<datalist id="datalist-Boosters"></datalist>
<script src="/webui/libs/jquery-3.6.0.min.js"></script>
<script src="/webui/libs/whirlpool-js.min.js"></script>
<script src="/webui/libs/single.js"></script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More